home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 37
/
Aminet 37 (2000)(Schatztruhe)[!][Jun 2000].iso
/
Aminet
/
game
/
misc
/
WormWars.lha
/
WormWars
/
Source
/
engine.c
< prev
next >
Wrap
C/C++ Source or Header
|
2000-04-26
|
151KB
|
4,162 lines
/* $Filename: WormWars/Source/engine.c $
* $VER: WormWars 5.61 $
*
* © Copyright 2000 James R. Jacobs.
*/
#include <string.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h> /* EXIT_SUCCESS, EXIT_FAILURE */
#include <assert.h>
#include "stdafx.h"
#include "diff.h"
#include "same.h"
#include "engine.h"
/* PRIVATE STRUCTURES -------------------------------------------------- */
struct
{ SBYTE x, y, deltax, deltay;
ABOOL alive, moved, teleported, visible, reflected;
} bullet[9];
struct
{ UWORD freq;
ULONG score;
} object[LASTOBJECT + 1] =
{ {1280, 60}, // AFFIXER
{ 60, 20}, // AMMO
{ 110, 20}, // ARMOUR
{ 70, 50}, // BIAS
{ 160, 30}, // BOMB
{ 80, 10}, // BONUS
{1020, 60}, // CLOCK
{ 250, 90}, // CYCLONE
{ 330, 50}, // GROWER
{1900, 90}, // HEALER
{1360, 60}, // ICE
{ 140, 60}, // LIFE
{ 160, 80}, // LIGHTNING
{ 970, 80}, // MAGNET
{ 240, 40}, // MISSILE
{ 640, 50}, // MULTIPLIER
{ 400, 10}, // NITRO
{ 240, 30}, // POWER
{ 480, 50}, // PROTECTOR
{ 210, 40}, // PULSE
{ 400, 40}, // REMNANTS
{ 500, 30}, // SIDESHOT
{ 600, 40}, // SLAYER
{ 980, 40}, // SLOWER
{ 730, 70}, // SWITCHER
{ 320, 20}, // TONGUE
{1500, 120}, // TREASURE
{3900, 140} // UMBRELLA
};
/* -200 common
220-400 uncommon
420-980 rare
1000+ very rare */
struct
{ SBYTE x, y, deltax, deltay, relx, rely;
ABOOL alive, last, visible;
} protector[4][PROTECTORS + 1];
struct
{ SBYTE deltax, deltay;
} thewormqueue[4][WORMQUEUELIMIT + 1];
struct
{ SBYTE deltax, deltay;
} thedogqueue[CREATURES + 1][DOGQUEUELIMIT + 1];
struct
{ ABOOL alive;
SBYTE x, y, player;
UBYTE object;
} magnet[MAGNETS + 1];
SBYTE eachworm[4][2][9] =
{ { { GREENHEADUP, GREENHEADUP, GREENHEADUP,
GREENHEADLEFT, ANYTHING, GREENHEADRIGHT,
GREENHEADDOWN, GREENHEADDOWN, GREENHEADDOWN
},
{ GREENMODEUP, GREENMODEUP, GREENMODEUP,
GREENMODELEFT, ANYTHING, GREENMODERIGHT,
GREENMODEDOWN, GREENMODEDOWN, GREENMODEDOWN
} },
{ { REDHEADUP, REDHEADUP, REDHEADUP,
REDHEADLEFT, ANYTHING, REDHEADRIGHT,
REDHEADDOWN, REDHEADDOWN, REDHEADDOWN
},
{ REDMODEUP, REDMODEUP, REDMODEUP,
REDMODELEFT, ANYTHING, REDMODERIGHT,
REDMODEDOWN, REDMODEDOWN, REDMODEDOWN
} },
{ { BLUEHEADUP, BLUEHEADUP, BLUEHEADUP,
BLUEHEADLEFT, ANYTHING, BLUEHEADRIGHT,
BLUEHEADDOWN, BLUEHEADDOWN, BLUEHEADDOWN
},
{ BLUEMODEUP, BLUEMODEUP, BLUEMODEUP,
BLUEMODELEFT, ANYTHING, BLUEMODERIGHT,
BLUEMODEDOWN, BLUEMODEDOWN, BLUEMODEDOWN
} },
{ { YELLOWHEADUP, YELLOWHEADUP, YELLOWHEADUP,
YELLOWHEADLEFT, ANYTHING, YELLOWHEADRIGHT,
YELLOWHEADDOWN, YELLOWHEADDOWN, YELLOWHEADDOWN
},
{ YELLOWMODEUP, YELLOWMODEUP, YELLOWMODEUP,
YELLOWMODELEFT, ANYTHING, YELLOWMODERIGHT,
YELLOWMODEDOWN, YELLOWMODEDOWN, YELLOWMODEDOWN
} } };
/* Rules for variable types:
SBYTE is used for field coordinates and queue indexes
UBYTE is used for field contents
SWORD is used for frequencies
ULONG is used for scores */
struct
{ ABOOL alive, explode, visible;
SBYTE x, y, deltax, deltay, pos, time;
UBYTE mode, dormant, multi, speed, last, oldlast, species,
type; // type is relevant worm 0-3 (for drips, missiles and dogs)
SWORD armour, tongue, freq;
ULONG score;
} creature[CREATURES + 1];
// MODULE VARIABLES (used only within engine.c) ---------------------------
MODULE ABOOL letters[4][LETTERS + 1], trainer;
MODULE SBYTE freq, ice, lettertype, letterx, lettery, leveltype,
treasurer;
// GLOBAL VARIABLES (owned by engine.c, imported by system.c) -------------
AGLOBAL ABOOL clearthem = FALSE,
modified = FALSE;
AGLOBAL SBYTE a = GAMEOVER,
board[MAXLEVELS + 1][FIELDX + 1][FIELDY + 1],
players,
field[FIELDX + 1][FIELDY + 1],
level = 1, levels, reallevel,
startx[MAXLEVELS + 1], starty[MAXLEVELS + 1];
AGLOBAL SWORD secondsleft, secondsperlevel;
AGLOBAL TEXT pathname[81],
date[DATELENGTH + 1],
times[TIMELENGTH + 1];
AGLOBAL ULONG delay, r;
AGLOBAL struct HiScoreStruct hiscore[HISCORES + 1];
AGLOBAL struct TeleportStruct teleport[MAXLEVELS + 1][4];
AGLOBAL struct WormStruct worm[4];
/* FUNCTIONS --------------------------------------------------------------
NAME align -- right-justify a string within another string
SYNOPSIS align(STRPTR, SBYTE, TEXT);
FUNCTION Moves all text in a string to the right, padding with
spaces. Does not itself add a null terminator.
INPUTS string - pointer to the string of text
size - size in characters of the containing string
filler - what to pad the left of the string with
NOTE Null terminators are written over by this function, but that
does not matter, because calling functions use Text() with an
explicit length. This function only works with monospaced
fonts.
MODULE engine.c */
void align(STRPTR string, SBYTE size, TEXT filler)
{ SBYTE i, shift, length;
length = strlen((const char*) string);
shift = size - length;
for (i = 1; i <= length; i++)
*(string + size - i) = *(string + size - i - shift);
for (i = 0; i <= shift - 1; i++)
*(string + i) = filler;
}
MODULE ABOOL blocked(SBYTE which, SBYTE deltax, SBYTE deltay)
{ UBYTE c = field[xwrap(teleport[level][partner(which)].x + deltax)][ywrap(teleport[level][partner(which)].y + deltay)];
if ((c < STONE || c > GOAT) && c != METAL)
return FALSE;
else return TRUE;
}
MODULE void bombblast(SBYTE triggerer, SBYTE player, SBYTE centrex, SBYTE centrey)
{ SBYTE counter, downy, downymax, leftx, leftxmax, rightx, rightxmax, strength, uppy, uppymax, x, y;
ULONG score = 0;
effect(FXBOMBBLAST);
strength = BOMBADD + (rand() % BOMBRAND);
leftxmax = centrex - strength;
if (leftxmax < 0)
leftxmax = 0;
rightxmax = centrex + strength;
if (rightxmax > FIELDX)
rightxmax = FIELDX;
uppymax = centrey - strength;
if (uppymax < 0)
uppymax = 0;
downymax = centrey + strength;
if (downymax > FIELDY)
downymax = FIELDY;
leftx = centrex;
rightx = centrex;
uppy = centrey;
downy = centrey;
for (counter = 1; counter <= strength; counter++)
{ if (leftx > leftxmax)
{ leftx--;
for (y = uppy; y <= downy; y++)
score += squareblast(triggerer, player, field[leftx][y], leftx, y);
}
if (rightx < rightxmax)
{ rightx++;
for (y = uppy; y <= downy; y++)
score += squareblast(triggerer, player, field[rightx][y], rightx, y);
}
if (uppy > uppymax)
{ uppy--;
for (x = leftx; x <= rightx; x++)
score += squareblast(triggerer, player, field[x][uppy], x, uppy);
}
if (downy < downymax)
{ downy++;
for (x = leftx; x <= rightx; x++)
score += squareblast(triggerer, player, field[x][downy], x, downy);
} }
if (triggerer == HEAD)
wormscore(player, score);
elif (triggerer == ORB)
orbscore(player, score);
if (worm[player].bias)
stat(player, LIFE);
}
MODULE void bouncegoat(SBYTE which, SBYTE x, SBYTE y)
{ if (field[x][y] == GOAT)
{ creature[whichcreature(x, y, GOAT, 255)].alive = FALSE;
orbscore(which, KILLGOAT);
change(x, y, BONUS);
} }
MODULE ABOOL bounceorb(SBYTE which, SBYTE x, SBYTE y)
{ if (field[x][y] == METAL)
return TRUE;
elif (creature[which].mode == NONE)
{ if (field[x][y] >= FIRSTNONE && field[x][y] <= LASTNONE)
return TRUE;
else return FALSE;
} elif (creature[which].mode == TONGUE)
{ if (field[x][y] >= FIRSTTONGUE && field[x][y] <= LASTTONGUE)
return TRUE;
else return FALSE;
} else
{ /* assert(creature[which].mode == ARMOUR); */
if (field[x][y] >= FIRSTARMOUR && field[x][y] <= LASTARMOUR)
return TRUE;
else return FALSE;
} }
MODULE SBYTE bsign(SBYTE value)
{ if (value < 0)
return (-1);
elif (value > 0)
return (1);
else return (0);
}
MODULE void changefield(void)
{ SBYTE x, y;
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
field[x][y] = board[level][x][y];
}
AGLOBAL void clearhiscores(void)
{ SBYTE i;
clearthem = FALSE;
for (i = 0; i <= HISCORES; i++)
{ hiscore[i].player = -1;
hiscore[i].level = 0;
hiscore[i].score = 0;
hiscore[i].fresh = FALSE;
hiscore[i].name[0] = 0;
hiscore[i].time[0] = 0;
hiscore[i].date[0] = 0;
} }
MODULE void clearletters(void)
{ SBYTE player, which;
for (player = 0; player <= 3; player++)
for (which = 0; which <= LETTERS; which++)
{ letters[player][which] = FALSE;
drawletter(player, FIRSTLETTER + which, BLACK);
} }
MODULE void copyfield(SBYTE source, SBYTE destination)
{ SBYTE which, x, y;
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
board[destination][x][y] = board[source][x][y];
startx[destination] = startx[source];
starty[destination] = starty[source];
for (which = 0; which <= 1; which++)
{ teleport[destination][which].alive = teleport[source][which].alive;
teleport[destination][which].x = teleport[source][which].x;
teleport[destination][which].y = teleport[source][which].y;
} }
MODULE void death(void)
{ SBYTE pain, player, which;
ABOOL slow;
for (player = 0; player <= 3; player++)
{ if (worm[player].lives)
{ if (!worm[player].alive)
{ slow = FALSE;
pain = 0;
if (worm[player].cause >= FIRSTTAIL && worm[player].cause <= LASTTAIL)
{ if (player == worm[player].cause - FIRSTTAIL)
pain = TAILPAIN;
else pain = OTHERTAILPAIN;
slow = TRUE;
} elif (worm[player].cause >= FIRSTFIRE && worm[player].cause <= LASTFIRE)
pain = WORMFIREPAIN;
elif (worm[player].cause >= FIRSTHEAD && worm[player].cause <= LASTHEAD)
pain = HEADPAIN;
elif (worm[player].cause >= FIRSTPROTECTOR && worm[player].cause <= LASTPROTECTOR)
pain = PROTECTORPAIN;
elif (worm[player].cause >= FIRSTMISSILE && worm[player].cause <= LASTMISSILE)
pain = MISSILEPAIN;
elif (worm[player].cause >= FIRSTDRIP && worm[player].cause <= LASTDRIP)
pain = DRIPPAIN;
else switch (worm[player].cause)
{
case BOMB:
pain = BOMBPAIN;
break;
case WOOD:
pain = WOODPAIN;
slow = TRUE;
break;
case FRAGMENT:
pain = FRAGMENTPAIN;
break;
case GOAT:
pain = GOATPAIN;
slow = TRUE;
break;
case SLAYER:
pain = SLAYERPAIN;
break;
case STONE:
pain = STONEPAIN;
slow = TRUE;
break;
case TELEPORT:
pain = TELEPORTPAIN;
slow = TRUE;
break;
case SLIME:
pain = SLIMEPAIN;
slow = TRUE;
break;
case METAL:
pain = METALPAIN;
slow = TRUE;
break;
case REMNANTS:
pain = REMNANTPAIN;
slow = TRUE;
break;
case LIGHTNING:
pain = LIGHTNINGPAIN;
break;
case PENGUIN:
pain = PENGUINPAIN;
break;
case WHIRLWIND:
pain = WHIRLWINDPAIN;
break;
case DOG:
pain = DOGPAIN;
break;
case CLOUD:
pain = CLOUDPAIN;
break;
default:
/* assert(0); */
break;
}
if (worm[player].victor >= 0 && worm[player].victor != player)
{ wormscore(worm[player].victor, KILLWORM);
if (worm[worm[player].victor].bias)
{ worm[worm[player].victor].lives += pain;
stat(worm[player].victor, LIFE);
} }
if (slow)
{ worm[player].speed = slowdown(worm[player].speed);
stat(player, NITRO);
}
if (pain > worm[player].lives)
worm[player].lives = 0;
else worm[player].lives -= pain;
draw(worm[player].x, worm[player].y, SKULL);
drawcause(player, NORMAL);
stat(player, LIFE);
if (level)
worm[player].levelreached = level;
else worm[player].levelreached = reallevel;
if (worm[player].lives)
{ effect(FXPAIN + player);
worm[player].alive = TRUE;
worm[player].causewait = r + CAUSEWAIT;
} else
{ /* kill worm */
effect(FXWORMDEATH);
if (ice == player)
ice = -1;
field[worm[player].x][worm[player].y] = SKULL;
for (which = 0; which <= PROTECTORS; which++)
if (protector[player][which].alive && protector[player][which].visible)
change(protector[player][which].x, protector[player][which].y, EMPTY);
for (which = 0; which <= MAGNETS; which++)
if (player == magnet[which].player)
magnet[which].alive = FALSE;
if (worm[player].score >= worm[player].hiscore)
worm[player].hiscore = worm[player].score;
} } } }
if (!worm[0].lives && !worm[1].lives && !worm[2].lives && !worm[3].lives)
{ /* End of game */
for (player = 0; player <= 3; player++)
if (worm[player].control != NONE && worm[player].score >= worm[player].hiscore)
worm[player].hiscore = worm[player].score;
newhiscores();
effect(FXDEFEAT);
a = GAMEOVER;
if (players == 1)
say((STRPTR) "Game over!", worm[onlyworm(FALSE)].colour);
elif (worm[0].control && ((!worm[1].control) || worm[1].score < worm[0].score) && ((!worm[2].control) || worm[2].score < worm[0].score) && ((!worm[3].control) || worm[3].score < worm[0].score))
say((STRPTR) "Green wins!", GREEN);
elif (worm[1].control && ((!worm[0].control) || worm[0].score < worm[1].score) && ((!worm[2].control) || worm[2].score < worm[1].score) && ((!worm[3].control) || worm[3].score < worm[1].score))
say((STRPTR) "Red wins!", RED);
elif (worm[2].control && ((!worm[0].control) || worm[0].score < worm[2].score) && ((!worm[1].control) || worm[1].score < worm[2].score) && ((!worm[3].control) || worm[3].score < worm[2].score))
say((STRPTR) "Blue wins!", BLUE);
elif (worm[3].control && ((!worm[0].control) || worm[0].score < worm[3].score) && ((!worm[1].control) || worm[1].score < worm[3].score) && ((!worm[2].control) || worm[2].score < worm[3].score))
say((STRPTR) "Yellow wins!", YELLOW);
else say((STRPTR) "A draw!", WHITE);
waitasec();
anykey(FALSE);
} }
MODULE void drawcause(SBYTE player, SBYTE state)
{ if (state == BLACK)
draw(-2 + ((FIELDX + 4) * worm[player].statx), (FIELDY / 2) - CAUSEYDISTANCE + (worm[player].staty * CAUSEYDISTANCE * 2), BLACKENED);
else draw(-2 + ((FIELDX + 4) * worm[player].statx), (FIELDY / 2) - CAUSEYDISTANCE + (worm[player].staty * CAUSEYDISTANCE * 2), worm[player].cause);
}
MODULE void drawletter(SBYTE player, SBYTE letter, SBYTE state)
{ UBYTE c;
if (state == BLACK)
c = BLACKENED;
else c = letter;
if (!worm[player].statx)
if (!worm[player].staty)
draw(-7 + ((letter - FIRSTLETTER) % 4),
(FIELDY / 2) - 2 + ((letter - FIRSTLETTER) / 4),
c);
else
draw(-7 + ((letter - FIRSTLETTER) % 4),
(FIELDY / 2) + 1 + ((letter - FIRSTLETTER) / 4),
c);
else
if (!worm[player].staty)
draw(FIELDX + 4 + ((letter - FIRSTLETTER) % 4),
(FIELDY / 2) - 2 + ((letter - FIRSTLETTER) / 4),
c);
else
draw(FIELDX + 4 + ((letter - FIRSTLETTER) % 4),
(FIELDY / 2) + 1 + ((letter - FIRSTLETTER) / 4),
c);
}
/* NAME enginesetup -- once-only initialization of engine variables
SYNOPSIS enginesetup(void);
FUNCTION Sets up the unchanging worm variables.
MODULE engine.c */
void enginesetup(void)
{ worm[0].statx = worm[0].staty = worm[1].staty = worm[2].statx = 0;
worm[1].statx = worm[2].staty = worm[3].statx = worm[3].staty = 1;
worm[0].colour = GREEN;
worm[1].colour = RED;
worm[2].colour = BLUE;
worm[3].colour = YELLOW;
worm[0].name[0] = worm[1].name[0] = worm[2].name[0] = worm[3].name[0] = 0;
strcpy(pathname, DEFAULTSET);
systemsetup();
}
/* NAME fastloop -- things done often
SYNOPSIS fastloop(void);
FUNCTION Checks for and handles level completion.
MODULE engine.c */
MODULE void fastloop(void)
{ ABOOL complete;
SBYTE advancer = -1, player, which;
/* update joystick `1' */
joy0();
/* flash letter */
if (level)
if (r % 8 == 1)
draw(letterx, lettery, WHITENED);
elif (r % 8 == 2)
draw(letterx, lettery, lettertype);
/* handle level completion */
for (player = 0; player <= 3; player++)
{ complete = TRUE;
for (which = 0; which <= LETTERS; which++)
if (!letters[player][which])
complete = FALSE;
if (complete)
advancer = player;
}
if (advancer != -1)
{ if (level++ == 0)
{ level = reallevel + 1;
reallevel = 0;
}
stopfx();
if (level > levels)
effect(FXVICTORY);
newlevel(advancer);
} }
void fillfield(SBYTE which)
{ SBYTE x, y;
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
{ board[level][x][y] = which;
draw(x, y, which);
}
board[level][startx[level]][starty[level]] = EMPTY;
draw(startx[level], starty[level], START);
if (teleport[level][0].alive)
{ board[level][teleport[level][0].x][teleport[level][0].y] = TELEPORT;
draw(teleport[level][0].x, teleport[level][0].y, ONE);
}
if (teleport[level][1].alive)
{ board[level][teleport[level][1].x][teleport[level][1].y] = TELEPORT;
draw(teleport[level][1].x, teleport[level][1].y, TWO);
} }
MODULE ABOOL findempty(SBYTE* x, SBYTE* y, SBYTE first, SBYTE last)
{ SBYTE count = 0, xx, yy;
do
{ xx = rand() % (FIELDX + 1);
yy = rand() % (FIELDY + 1);
} while ((field[xx][yy] < first || field[xx][yy] > last) && ++count < PATIENCE);
if (count < PATIENCE)
{ *x = xx;
*y = yy;
return(TRUE);
} else return(FALSE);
}
void gameloop(void)
{ SBYTE player, which;
if (a == PLAYGAME)
{ fastloop();
gameinput();
}
if (a == PLAYGAME)
for (player = 0; player <= 3; player++)
if (worm[player].lives && (!(r % worm[player].speed)) && (ice == -1 || ice == player))
wormloop(player);
if (a == PLAYGAME)
for (which = 0; which <= CREATURES; which++)
if (creature[which].alive && (!(r % creature[which].speed)) && (ice == -1 || (creature[which].species == MISSILE && ice == creature[which].type)))
creatureloop(which);
if (a == PLAYGAME && !(r % MAGNETSPEED))
magnetloop();
if (a == PLAYGAME)
death();
if (a == PLAYGAME)
if (ice == -1 && (!(r % VERYSLOW)))
slowloop();
elif (--worm[ice].ice == 0)
ice = -1;
timing();
}
MODULE void killall(void)
{ UBYTE i;
for (i = 0; i <= CREATURES; i++)
creature[i].alive = FALSE;
for (i = 0; i <= MAGNETS; i++)
magnet[i].alive = FALSE;
teleport[level][2].alive = FALSE;
teleport[level][3].alive = FALSE;
}
void levelappend(void)
{ UBYTE oldlevel;
if (levels < MAXLEVELS)
{ oldlevel = level;
level = ++levels;
newfield();
level = oldlevel;
saylevel(WHITE);
} }
void leveldelete(void)
{ SBYTE i;
/* pull boards
*/
if (levels > 1)
{ if (level < levels)
for (i = level; i < levels; i++)
copyfield(i + 1, i);
else
level--;
levels--;
saylevel(WHITE);
turborender();
} }
void levelerase(void)
{ newfield();
turborender();
}
void levelinsert(void)
{ SBYTE i;
/* push boards
*/
if (levels < MAXLEVELS)
{ for (i = levels; i >= level; i--)
copyfield(i, i + 1);
levels++;
saylevel(WHITE);
newfield();
turborender();
} }
SBYTE loadfields(STRPTR fieldname)
{ SBYTE i, j, x, y;
TEXT IOBuffer[NAMELENGTH + 1];
UBYTE ver;
/* This routine is not entirely robust, especially regarding
failures part way through reading. Also, field data values must be
those supported by the field editor (ie. objects, and the squares
represented by F1-F8), or undefined behaviour may result. None of
this is currently checked for. Provided that the fieldset was
created with the official field editor, and the file is not
corrupt, these failures should never happen anyway.
open file */
if (!ZOpen(fieldname, FALSE))
return 1; /* no harm done */
/* read header */
if (!ZRead(IOBuffer, 10))
{ ZClose();
return 2; /* no harm done */
}
if (!strcmp(IOBuffer, "FSET 5.6"))
ver = 56;
elif (!strcmp(IOBuffer, "FSET 5.5"))
ver = 55;
elif (!strcmp(IOBuffer, "FSET 5.3"))
ver = 53;
elif (!strcmp(IOBuffer, "FSET 5.1"))
ver = 51;
elif (!strcmp(IOBuffer, "FSET 5.0"))
ver = 50;
elif (!strcmp(IOBuffer, "FSET 4.4"))
ver = 44;
else
{ ZClose();
return 3; /* no harm done */
}
levels = IOBuffer[9];
/* read high score table */
for (i = 0; i <= HISCORES; i++)
{ if (!ZRead(IOBuffer, 6))
{ ZClose();
return 4; /* incorrect levels */
}
hiscore[i].fresh = FALSE;
hiscore[i].player = IOBuffer[0];
hiscore[i].level = IOBuffer[1];
hiscore[i].score = (IOBuffer[3] * 65536)
+ (IOBuffer[4] * 256)
+ IOBuffer[5];
if (!ZRead(IOBuffer, NAMELENGTH + 1))
{ ZClose();
return 5; /* incorrect levels, corrupted high scores */
}
for (j = 0; j <= NAMELENGTH; j++)
hiscore[i].name[j] = IOBuffer[j];
if (ver >= 50)
{ if (!ZRead(IOBuffer, TIMELENGTH + 1))
{ ZClose();
return 6; /* incorrect levels, corrupted high scores */
}
for (j = 0; j <= TIMELENGTH; j++)
hiscore[i].time[j] = IOBuffer[j];
if (!ZRead(IOBuffer, DATELENGTH + 1))
{ ZClose();
return 7; /* incorrect levels, corrupted high scores */
}
for (j = 0; j <= DATELENGTH; j++)
hiscore[i].date[j] = IOBuffer[j];
} else
{ /* skip extra name character */
if (!ZRead(IOBuffer, 1))
{ ZClose();
return 8; /* incorrect levels, corrupted high scores */
} else
{ if (hiscore[i].name[0])
{ strcpy(hiscore[i].time, "??:??");
strcpy(hiscore[i].date, "??/??/??");
} else
{ hiscore[i].time[0] = 0;
hiscore[i].date[0] = 0;
} } } }
/* read level data */
for (i = 0; i <= levels; i++)
{ if (!ZRead(IOBuffer, 8))
{ ZClose();
return 9;
/* incorrect levels, corrupted high scores,
incorrect startx, teleports, field data */
}
startx[i] = IOBuffer[0];
starty[i] = IOBuffer[1];
teleport[i][0].alive = IOBuffer[2];
teleport[i][0].x = IOBuffer[3];
teleport[i][0].y = IOBuffer[4];
teleport[i][1].alive = IOBuffer[5];
teleport[i][1].x = IOBuffer[6];
teleport[i][1].y = IOBuffer[7];
if (!ZRead((char *) &board[i][0][0], (FIELDX + 1) * (FIELDY + 1)))
{ ZClose();
return 10;
/* incorrect levels, corrupted high scores,
incorrect startx, teleports, field data */
} else
{ if (ver <= 44)
{ // convert from FSET 4.4 to FSET 5.0 format
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (board[i][x][y] >= 16)
board[i][x][y] += 2;
}
if (ver <= 50)
{ // convert from FSET 5.0 to FSET 5.1 format
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (board[i][x][y] >= 11)
board[i][x][y]++;
}
if (ver <= 51)
{ // convert from FSET 5.1 to FSET 5.3 format
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (board[i][x][y] >= 7 && board[i][x][y] <= 63)
board[i][x][y]++;
}
if (ver <= 53)
{ // convert from FSET 5.3 to FSET 5.5 format
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (board[i][x][y] >= 20)
board[i][x][y]++;
}
if (ver <= 55)
{ // convert from FSET 5.5 to FSET 5.6 format
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (board[i][x][y] >= 13)
board[i][x][y]++;
} } }
// no need to read version string
ZClose();
modified = FALSE;
return 0;
}
void matchteleports(void)
{ SBYTE which;
for (which = 0; which <= levels; which++)
if (teleport[which][0].alive == TRUE && teleport[which][1].alive == FALSE)
{ board[which][teleport[which][0].x][teleport[which][0].y] = EMPTY;
teleport[which][0].alive = FALSE;
if (level == which && a == FIELDEDIT)
draw(teleport[which][0].x, teleport[which][0].y, EMPTY);
} elif (teleport[which][0].alive == FALSE && teleport[which][1].alive == TRUE)
{ board[which][teleport[which][1].x][teleport[which][1].y] = EMPTY;
teleport[which][1].alive = FALSE;
if (level == which && a == FIELDEDIT)
draw(teleport[which][1].x, teleport[which][1].y, EMPTY);
} }
MODULE void newfield(void)
{ int x, y;
teleport[level][0].alive = FALSE;
teleport[level][1].alive = FALSE;
startx[level] = FIELDX / 2;
starty[level] = FIELDY / 2;
if (level)
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
board[level][x][y] = EMPTY;
else for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
board[0][x][y] = SILVER;
}
void newfields(void)
{ if (verify())
{ strcpy(pathname, DEFAULTSET);
levels = DEFAULTLEVELS;
modified = FALSE;
for (level = 0; level <= levels; level++)
newfield();
clearhiscores();
level = 1;
if (a == FIELDEDIT)
{ turborender();
saylevel(WHITE);
} else hiscores();
} }
void newgame(void)
{ SBYTE player;
players = 0;
for (player = 0; player <= 3; player++)
if (worm[player].control != NONE)
players++;
for (player = 0; player <= 3; player++)
{ worm[player].lives = 0;
worm[player].speed = NORMAL;
worm[player].hiscore = 0;
}
r = -1;
trainer = FALSE;
ice = -1;
reallevel = 0;
level = 1;
a = PLAYGAME;
clearscreen();
newlevel(rand() % 4);
timing();
}
MODULE void newhiscores(void)
{ PERSIST TEXT amiganame[4][NAMELENGTH + 1] = {"Jay Miner", "Carl Sassenrath", "R. J. Mical", "Dave Morse"};
AUTO SBYTE i, j, player;
datestamp();
for (player = 0; player <= 3; player++)
for (i = 0; i <= HISCORES; i++)
if (worm[player].control != NONE && worm[player].score >= hiscore[i].score)
{ /* push all worse hiscores down */
if (i < HISCORES)
for (j = HISCORES; j >= i + 1; j--)
{ hiscore[j].player = hiscore[j - 1].player;
hiscore[j].score = hiscore[j - 1].score;
hiscore[j].level = hiscore[j - 1].level;
hiscore[j].fresh = hiscore[j - 1].fresh;
strcpy(hiscore[j].name, hiscore[j - 1].name);
strcpy(hiscore[j].date, hiscore[j - 1].date);
strcpy(hiscore[j].time, hiscore[j - 1].time);
}
modified = TRUE;
hiscore[i].player = player;
hiscore[i].score = worm[player].hiscore;
hiscore[i].level = worm[player].levelreached;
if (worm[player].control == AMIGA)
{ strcpy(hiscore[i].name, amiganame[player]);
hiscore[i].fresh = FALSE;
} else
{ strcpy(hiscore[i].name, "(New)");
hiscore[i].fresh = TRUE;
}
strcpy(hiscore[i].time, times);
strcpy(hiscore[i].date, date);
break; /* vital
*/
} }
MODULE void newlevel(SBYTE player)
{ SBYTE iwhich, which;
if (level >= 2 && worm[player].lives)
rundown(player);
if (a == PLAYGAME)
{ if (level > levels)
{ for (which = 0; which <= 3; which++)
if (worm[which].lives)
worm[which].levelreached = -1;
celebrate();
newhiscores();
titlescreen();
} else
{ saylevel(WHITE);
for (which = 0; which <= 3; which++)
worm[which].multi = (SBYTE) atleast(worm[which].multi / 2, 1);
killall();
clearletters();
orientworms();
changefield();
turborender();
delay = atleast(DELAY_MAX - (level * DELAY_DEC), DELAY_MIN);
if (level)
{ secondsperlevel = SECONDSPERLEVEL;
putletter(-1);
freq = (SBYTE) atleast(FREQ_MAX - ((level - 1) * FREQ_DEC), FREQ_MIN);
for (which = 0; which <= 3; which++)
{ if (!worm[which].lives && worm[which].control != NONE)
{ /* create (or resurrect) a worm
*/
worm[which].lives = STARTLIVES;
worm[which].score = 0;
worm[which].alive = TRUE;
worm[which].oldscore = 0;
worm[which].armour = 0;
worm[which].tongue = 0;
worm[which].nitro = FALSE;
worm[which].mode = NULL;
worm[which].power = 0;
worm[which].bias = 0;
worm[which].multi = 1;
worm[which].ice = 0;
worm[which].victor = -1;
worm[which].ammo = 0;
worm[which].remnants = FALSE;
worm[which].affixer = FALSE;
worm[which].sideshot = FALSE;
worm[which].causewait = (ULONG) -1;
worm[which].last = FIRSTTAIL + which;
worm[which].pos = -1;
for (iwhich = 0; iwhich <= PROTECTORS; iwhich++)
protector[which][iwhich].alive = FALSE;
for (iwhich = 0; iwhich <= LASTOBJECT; iwhich++)
stat(which, iwhich);
} } } } }
clearjoystick();
clearkybd();
resettime();
}
MODULE void explosion(SBYTE x, SBYTE y, SBYTE exceptionx, SBYTE exceptiony)
{ UBYTE i, generated = 0;
/* You wouldn't think this would work properly for pulse-explosions,
because the worm's head is obliterated. However, it is refreshed (as
tail) the next time wormloop() is called for that worm. */
effect(FXEXPLODE);
for (i = 0; i <= CREATURES; i++)
if ((!(creature[i].alive)) && generated <= 7)
{ creature[i].last = EMPTY;
creature[i].x = x;
creature[i].y = y;
if (level)
creature[i].speed = (SBYTE) atleast(FRAGSPEED - (level / 2), 1);
else creature[i].speed = BONUSFRAGSPEED;
creature[i].species = FRAGMENT;
creature[i].visible = TRUE;
switch (generated)
{
case 0:
creature[i].deltax = 0;
creature[i].deltay = -1;
break;
case 1:
creature[i].deltax = 1;
creature[i].deltay = -1;
break;
case 2:
creature[i].deltax = 1;
creature[i].deltay = 0;
break;
case 3:
creature[i].deltax = 1;
creature[i].deltay = 1;
break;
case 4:
creature[i].deltax = 0;
creature[i].deltay = 1;
break;
case 5:
creature[i].deltax = -1;
creature[i].deltay = 1;
break;
case 6:
creature[i].deltax = -1;
creature[i].deltay = 0;
break;
case 7:
creature[i].deltax = -1;
creature[i].deltay = -1;
break;
default:
break;
}
generated++;
if (creature[i].deltax != exceptionx || creature[i].deltay != exceptiony)
{ creature[i].alive = TRUE;
if (generated == 1)
change(x, y, FRAGMENT);
} } }
/* Many creatures take advantage of shared characteristics.
10 creature types (orbs, goats, drips, fragments, missiles, penguins,
cyclones, dogs, clouds, timebombs) use the creature structure.
Independent of it are worms, protectors, worm bullets and teleports. */
MODULE void creatureloop(SBYTE which)
{ ABOOL happy = FALSE;
UBYTE bestdistance = 255, distance, player, c, i;
SBYTE x, y, xx, yy, xxx, yyy, frontx, fronty, rearx, reary;
x = creature[which].x;
y = creature[which].y;
if (creature[which].species == ORB && creature[which].explode)
{ creature[which].alive = FALSE;
explosion(x, y, -2, -2);
return;
}
/* decide whether and where to move
*/
switch(creature[which].species)
{
case CLOUD:
if (creature[which].x == 0 || creature[which].x == FIELDX)
creature[which].deltax = -creature[which].deltax;
break;
case TIMEBOMB:
/* decrement and explode timebombs */
if (field[x][y] != TIMEBOMB)
creature[which].alive = FALSE;
else
{ effect(FXTIMEBOMBTICK);
creature[which].time--;
if (creature[which].time < 0)
{ creature[which].alive = FALSE;
bombblast(BOMB, 0, x, y);
change(x, y, EMPTY);
} else draw(x, y, ZERO + creature[which].time);
}
return;
break;
case DOG:
/* remove a movement from the dog queue */
if (creature[which].dormant == CHASING)
{ if (creature[which].pos != -1)
{ creature[which].deltax = thedogqueue[which][0].deltax;
creature[which].deltay = thedogqueue[which][0].deltay;
if (--creature[which].pos != -1)
{ for (i = 0; i <= creature[which].pos; i++)
{ thedogqueue[which][i].deltax = thedogqueue[which][i + 1].deltax;
thedogqueue[which][i].deltay = thedogqueue[which][i + 1].deltay;
} } }
else creature[which].alive = FALSE;
}
break;
case PENGUIN:
do
{ xx = (rand() % 3) - 1;
yy = (rand() % 3) - 1;
} while (!valid(x + xx, y + yy));
c = field[x + xx][y + yy];
if (c >= FIRSTEMPTY && c <= LASTEMPTY)
{ creature[which].deltax = xx;
creature[which].deltay = yy;
} else
{ creature[which].deltax = 0;
creature[which].deltay = 0;
}
break;
case WHIRLWIND:
/* Whirlwinds has a slight upwards drift.
Higher values of UNDRIFT make it less buoyant. */
creature[which].deltax = (rand() % 3) - 1;
if (!(rand() % UNDRIFT))
creature[which].deltay = (rand() % 2) - 1;
else creature[which].deltay = (rand() % 3) - 1;
break;
case MISSILE:
for (player = 0; player <= 3; player++)
if (creature[which].type != player && worm[player].lives && !worm[player].bias)
{ xx = abs(worm[player].x - x);
yy = abs(worm[player].y - y);
if (xx < yy)
distance = xx;
else distance = yy;
if (distance <= bestdistance)
{ bestdistance = distance;
creature[which].deltax = bsign(worm[player].x - x);
creature[which].deltay = bsign(worm[player].y - y);
} }
for (i = 0; i <= CREATURES; i++)
if (creature[i].alive && which != i)
{ if
( creature[i].species == ORB
|| (creature[i].species == MISSILE && creature[i].type != creature[which].type)
|| creature[i].species == GOAT
)
{ xx = abs(creature[i].x - x);
yy = abs(creature[i].y - y);
if (xx < yy)
distance = xx;
else distance = yy;
if (distance <= bestdistance)
{ bestdistance = distance;
creature[which].deltax = bsign(creature[i].x - x);
creature[which].deltay = bsign(creature[i].y - y);
} } }
if (bestdistance == 255)
creature[which].alive = FALSE;
break;
case ORB:
frontx = xwrap(x + creature[which].deltax); /* look in front */
fronty = ywrap(y + creature[which].deltay);
rearx = xwrap(x - creature[which].deltax); /* look behind */
reary = ywrap(y - creature[which].deltay);
if (bounceorb(which, frontx, fronty))
{ bouncegoat(which, frontx, fronty);
xx = -creature[which].deltax; /* default bounce angle is 180° */
yy = -creature[which].deltay;
if (!bounceorb(which, frontx, reary))
{ if (bounceorb(which, rearx, fronty))
{ bouncegoat(which, rearx, fronty);
xx = creature[which].deltax;
} }
elif (!bounceorb(which, rearx, fronty))
{ bouncegoat(which, rearx, fronty);
yy = creature[which].deltay;
}
creature[which].deltax = xx;
creature[which].deltay = yy;
}
break;
case GOAT:
/* decide whether to move
*/
if (!(rand() % GOATMOVE))
{ for (xx = x - 1; xx <= x + 1; xx++)
for (yy = y - 1; yy <= y + 1; yy++)
if (valid(xx, yy) && field[xx][yy] >= FIRSTEMPTY && field[xx][yy] <= LASTEMPTY)
{ happy = TRUE;
break;
} }
creature[which].deltax = 0;
creature[which].deltay = 0;
if (!happy)
{ xx = (rand() % 3) - 1;
yy = (rand() % 3) - 1;
if (valid(x + xx, y + yy) && (xx || yy))
{ c = field[x + xx][y + yy];
if (c >= FIRSTGOAT && c <= LASTGOAT && c != GOAT)
{ creature[which].last = creature[which].oldlast;
creature[which].oldlast = c;
creature[which].deltax = xx;
creature[which].deltay = yy;
} } }
break;
default:
break;
}
/* now move */
if (creature[which].deltax || creature[which].deltay)
{ if (creature[which].visible)
{ /* erase previous image */
change(x, y, creature[which].last);
if (creature[which].species != FRAGMENT)
creature[which].last = EMPTY;
}
if (creature[which].alive)
{ creature[which].x += creature[which].deltax;
creature[which].y += creature[which].deltay;
if (creature[which].species == ORB)
{ creature[which].x = xwrap(creature[which].x);
creature[which].y = ywrap(creature[which].y);
} elif (creature[which].species == FRAGMENT || creature[which].species == DRIP || creature[which].species == WHIRLWIND || creature[which].species == DOG)
{ if (!valid(creature[which].x, creature[which].y))
creature[which].alive = FALSE;
} } }
creature[which].visible = TRUE;
x = creature[which].x;
y = creature[which].y;
/* Collision detection.
Goats, penguins, timebombs don't need to go through this. */
if
( creature[which].alive
&& creature[which].species != GOAT
&& creature[which].species != PENGUIN
&& (creature[which].deltax || creature[which].deltay)
)
{ c = field[x][y];
if (c >= FIRSTHEAD && c <= LASTHEAD)
{ if (creature[which].species == MISSILE)
wormmissile(x, y, c - FIRSTHEAD, which);
elif (creature[which].species == DOG)
wormdog(x, y, c - FIRSTHEAD, which);
elif (creature[which].species == FRAGMENT)
wormfrag(x, y, c - FIRSTHEAD, which);
elif (creature[which].species == DRIP)
wormdrip(x, y, c - FIRSTHEAD, which);
elif (creature[which].species == WHIRLWIND)
wormwhirlwind(x, y, c - FIRSTHEAD, which);
elif (creature[which].species == CLOUD)
cloudworm(x, y, which, c - FIRSTHEAD);
elif (creature[which].species == ORB)
{ wormorb(x, y, c - FIRSTHEAD, which);
creature[which].last = c - FIRSTHEAD + FIRSTTAIL; /* note sign issues */
} }
elif (c == TIMEBOMB)
{ creature[whichcreature(x, y, which, TIMEBOMB)].alive = FALSE;
bombblast(BOMB, 0, x, y);
change(x, y, EMPTY);
} elif (c >= FIRSTTAIL && c <= LASTTAIL)
{ if (creature[which].species == ORB)
{ if (creature[which].mode == TONGUE)
effect(FXUSETONGUE);
else
{ if (worm[c - FIRSTTAIL].alive)
{ effect(FXORBDEATH);
wormscore(c - FIRSTTAIL, creature[which].score);
if (worm[c - FIRSTTAIL].bias)
{ worm[c - FIRSTTAIL].lives += ORBBLOOD;
stat(c - FIRSTTAIL, LIFE);
} }
creature[which].alive = FALSE;
change(x, y, BONUS);
} } }
elif (c >= FIRSTPROTECTOR && c <= LASTPROTECTOR)
{ if (creature[which].species == DOG)
protdog(x, y, c - FIRSTPROTECTOR, which);
elif (creature[which].species == DRIP)
protdrip(x, y, c - FIRSTPROTECTOR, which);
elif (creature[which].species == MISSILE)
protmissile(x, y, c - FIRSTPROTECTOR, which);
elif (creature[which].species == FRAGMENT)
protfrag(x, y, c - FIRSTPROTECTOR, which);
elif (creature[which].species == ORB)
protorb(x, y, c - FIRSTPROTECTOR, which);
elif (creature[which].species == PENGUIN)
protpenguin(x, y, c - FIRSTPROTECTOR, which);
elif (creature[which].species == WHIRLWIND)
protwhirlwind(x, y, c - FIRSTPROTECTOR, which);
elif (creature[which].species == CLOUD)
cloudprot(x, y, which, c - FIRSTPROTECTOR);
} elif (c >= FIRSTLETTER && c <= LASTLETTER)
{ if (creature[which].species == DRIP || creature[which].species == FRAGMENT || creature[which].species == MISSILE || creature[which].species == CLOUD)
{ effect(FXTHUD);
creature[which].alive = FALSE;
} elif (creature[which].species == ORB)
{ for (player = 0; player <= 3; player++)
{ letters[player][c - FIRSTLETTER] = FALSE;
drawletter(player, c, BLACK);
}
putletter(-1);
orbscore(which, LETTERPOINT);
} elif (creature[which].species == WHIRLWIND)
putletter(-1);
} elif (c >= FIRSTDRIP && c <= LASTDRIP)
{ i = whichcreature(x, y, DRIP, which);
if (creature[which].species == FRAGMENT)
dripfrag(x, y, i, which);
elif (creature[which].species == MISSILE)
dripmissile(x, y, i, which);
elif (creature[which].species == ORB)
driporb(x, y, i, which);
elif (creature[which].species == WHIRLWIND)
dripwhirlwind(x, y, i, which);
elif (creature[which].species == CLOUD)
clouddrip(x, y, which, i);
} elif (c >= FIRSTMISSILE && c <= LASTMISSILE)
{ i = whichcreature(x, y, MISSILE, which);
if (creature[which].species == FRAGMENT)
fragmissile(x, y, which, i);
elif (creature[which].species == DRIP)
dripmissile(x, y, which, i);
elif (creature[which].species == ORB)
orbmissile(x, y, which, i);
elif (creature[which].species == WHIRLWIND)
missilewhirlwind(x, y, i, which);
elif (creature[which].species == MISSILE)
missilemissile(x, y, which, i);
elif (creature[which].species == CLOUD)
cloudmissile(x, y, which, i);
} elif (c == WHIRLWIND)
{ i = whichcreature(x, y, WHIRLWIND, which);
if (creature[which].species == DRIP)
dripwhirlwind(x, y, which, i);
elif (creature[which].species == MISSILE)
missilewhirlwind(x, y, which, i);
elif (creature[which].species == ORB)
orbwhirlwind(x, y, which, i);
elif (creature[which].species == WHIRLWIND)
whirlwindwhirlwind(x, y, which, i);
elif (creature[which].species == FRAGMENT)
fragwhirlwind(x, y, which, i);
elif (creature[which].species == CLOUD)
cloudwhirlwind(x, y, which, i);
} elif (c == DOG)
{ i = whichcreature(x, y, DOG, which);
if (creature[which].species == FRAGMENT)
dogfrag(x, y, i, which);
elif (creature[which].species == PENGUIN)
dogpenguin(x, y, i, which);
elif (creature[which].species == ORB)
dogorb(x, y, i, which);
elif (creature[i].species == DRIP)
dogdrip(x, y, i, which);
elif (creature[i].species == WHIRLWIND)
dogwhirlwind(x, y, i, which);
elif (creature[i].species == MISSILE)
dogmissile(x, y, i, which);
elif (creature[i].species == DOG)
dogdog(x, y, i, which);
elif (creature[i].species == CLOUD)
clouddog(x, y, which, i);
} elif (c == PENGUIN)
{ i = whichcreature(x, y, PENGUIN, which);
if (creature[which].species == ORB)
orbpenguin(x, y, which, i);
elif (creature[which].species == DOG)
dogpenguin(x, y, which, i);
elif (creature[which].species == WHIRLWIND)
penguinwhirlwind(x, y, i, which);
elif (creature[which].species == MISSILE)
missilepenguin(x, y, which, i);
elif (creature[which].species == DRIP)
drippenguin(x, y, which, i);
elif (creature[which].species == CLOUD)
cloudpenguin(x, y, which, i);
} elif (c == FRAGMENT)
{ i = whichcreature(x, y, FRAGMENT, which);
if (creature[which].species == MISSILE)
fragmissile(x, y, i, which);
elif (creature[which].species == DRIP)
dripfrag(x, y, which, i);
elif (creature[which].species == ORB)
fragorb(x, y, i, which);
elif (creature[which].species == FRAGMENT)
fragfrag(x, y, which, i);
elif (creature[which].species == WHIRLWIND)
fragwhirlwind(x, y, i, which);
elif (creature[which].species == CLOUD)
cloudfrag(x, y, which, i);
elif (creature[which].species == DOG)
dogfrag(x, y, which, i);
} elif (c == METAL)
{ if (creature[which].species == DRIP || creature[which].species == MISSILE || creature[which].species == CLOUD)
creature[which].alive = FALSE;
elif (creature[which].species == FRAGMENT)
reflect(which);
} elif (c == STONE)
{ if (creature[which].species == DRIP || creature[which].species == FRAGMENT || creature[which].species == MISSILE || creature[which].species == CLOUD)
{ effect(FXTHUD);
creature[which].alive = FALSE;
} }
elif (c == WOOD)
{ if (creature[which].species == FRAGMENT || creature[which].species == MISSILE || creature[which].species == CLOUD || creature[which].species == DRIP)
{ effect(FXTHUD);
creature[which].alive = FALSE;
} }
elif (c == GOAT)
{ /* Drips, fragments, missiles, cyclones, dogs, clouds */
creature[whichcreature(x, y, GOAT, 255)].alive = FALSE;
effect(FXGOATDEATH);
if (creature[which].species != WHIRLWIND)
{ creature[which].alive = FALSE;
change(x, y, BONUS);
}
if (creature[which].species == MISSILE)
{ wormscore(creature[which].type, KILLGOAT);
if (worm[creature[which].type].bias)
{ worm[creature[which].type].lives += GOATBLOOD;
stat(creature[which].type, LIFE);
} } }
elif (c == SLIME)
{ if (creature[which].species == DRIP || creature[which].species == FRAGMENT || creature[which].species == MISSILE || (creature[which].species == ORB && creature[which].mode != ARMOUR))
{ creature[which].alive = FALSE;
change(x, y, EMPTY);
} }
elif (c == SKULL)
{ if (creature[which].species == ORB)
{ effect(FXGETSKULL);
orbscore(which, SKULLPOINT);
} elif (creature[which].species == FRAGMENT || creature[which].species == DRIP || creature[which].species == MISSILE || creature[which].species == CLOUD)
{ effect(FXTHUD);
creature[which].alive = FALSE;
} }
elif (c == TELEPORT)
{ /* Drips, fragments, missiles, orbs, whirlwinds, dogs, clouds
*/
i = whichteleport(x, y);
if (blocked(i, creature[which].deltax, creature[which].deltay))
creature[which].alive = FALSE;
else
{ effect(FXUSETELEPORT);
creature[which].x = teleport[level][partner(i)].x + creature[which].deltax;
creature[which].y = teleport[level][partner(i)].y + creature[which].deltay;
if (creature[which].species == ORB)
{ orbscore(which, TELPOINT);
creature[which].x = xwrap(creature[which].x);
creature[which].y = ywrap(creature[which].y);
} else
{ if (!(valid(creature[which].x, creature[which].y)))
creature[which].alive = FALSE;
if (creature[which].species == FRAGMENT)
creature[which].last = SILVER;
} } }
elif (c == ORB)
{ i = whichcreature(x, y, ORB, which);
if (creature[which].species == DRIP)
driporb(x, y, which, i);
elif (creature[which].species == FRAGMENT)
fragorb(x, y, which, i);
elif (creature[which].species == WHIRLWIND)
orbwhirlwind(x, y, i, which);
elif (creature[which].species == PENGUIN)
orbpenguin(x, y, i, which);
elif (creature[which].species == MISSILE)
orbmissile(x, y, i, which);
elif (creature[which].species == DOG)
dogorb(x, y, which, i);
elif (creature[which].species == ORB)
orborb(x, y, which, i);
elif (creature[which].species == CLOUD)
cloudorb(x, y, which, i);
} elif (c == CLOUD)
{ i = whichcreature(x, y, CLOUD, which);
if (creature[which].species == DRIP)
clouddrip(x, y, which, i);
elif (creature[which].species == FRAGMENT)
cloudfrag(x, y, which, i);
elif (creature[which].species == WHIRLWIND)
cloudwhirlwind(x, y, which, i);
elif (creature[which].species == PENGUIN)
cloudpenguin(x, y, which, i);
elif (creature[which].species == MISSILE)
cloudmissile(x, y, which, i);
elif (creature[which].species == DOG)
clouddog(x, y, which, i);
elif (creature[which].species == ORB)
cloudorb(x, y, which, i);
elif (creature[which].species == CLOUD)
cloudcloud(x, y, which, i);
} elif (creature[which].species == ORB)
{ if (c <= LASTOBJECT && c != SLAYER && c != BOMB)
{ effect(FXGETOBJECT);
orbscore(which, object[c].score);
}
if (c == EMPTY)
orbscore(which, EMPTYPOINT);
elif (c == SILVER)
orbscore(which, SILVERPOINT);
elif (c == GOLD)
orbscore(which, GOLDPOINT);
elif (c == AMMO || c == PULSE || c == SLAYER || c == LIGHTNING)
{ if (creature[which].mode != ARMOUR)
creature[which].explode = TRUE;
else effect(FXUSEARMOUR);
} elif (c == NITRO || c == POWER || c == SLOWER || c == CYCLONE)
{ effect(FXGETNITRO);
creature[which].speed = speedup(creature[which].speed, TRUE);
} elif (c == HEALER || c == LIFE || c == ICE || c == TREASURE || c == UMBRELLA)
orbsplit(which);
elif (c == BOMB)
{ draw(x, y, ORB);
bombblast(ORB, which, x, y);
} elif (c == ARMOUR)
{ creature[which].armour += MODEADD + (rand() % MODERAND);
creature[which].mode = ARMOUR;
} elif (c == TONGUE)
{ creature[which].tongue += MODEADD + (rand() % MODERAND);
creature[which].mode = TONGUE;
} elif (c == PROTECTOR)
{ for (player = 0; player <= 3; player++)
if (worm[player].lives)
for (i = 0; i <= PROTECTORS; i++)
if (protector[player][i].alive)
{ protector[player][i].alive = FALSE;
if (protector[player][i].visible)
change(protector[player][i].x, protector[player][i].y, EMPTY);
} }
elif (c == MISSILE)
{ for (i = 0; i <= CREATURES; i++)
{ if (creature[i].alive && creature[i].species == MISSILE)
{ creature[i].alive = FALSE;
change(x, y, EMPTY);
} } }
elif (c == MULTIPLIER)
{ creature[which].multi *= 2;
if (creature[which].multi > MULTILIMIT)
creature[which].multi = MULTILIMIT;
} elif (c == BIAS)
{ for (player = 0; player <= 3; player++)
if (worm[player].lives && worm[player].bias)
{ worm[player].bias = 0;
stat(player, BIAS);
} }
elif (c == AFFIXER)
{ for (player = 0; player <= 3; player++)
if (worm[player].lives)
worm[player].affixer = FALSE;
} elif (c == REMNANTS)
{ for (player = 0; player <= 3; player++)
if (worm[player].lives)
worm[player].remnants = FALSE;
} elif (c == SIDESHOT)
{ for (player = 0; player <= 3; player++)
if (worm[player].lives)
worm[player].sideshot = FALSE;
} elif (c == MAGNET)
{ for (i = 0; i <= MAGNETS; i++)
if (magnet[i].alive)
magnet[i].alive = FALSE;
} elif (c == SWITCHER)
{ for (xx = 0; xx <= FIELDX; xx++)
for (yy = 0; yy <= FIELDY; yy++)
if (field[xx][yy] >= FIRSTTAIL && field[xx][yy] <= LASTTAIL)
change(xx, yy, WOOD);
} elif (c == GROWER)
{ effect(FXGETGROWER);
for (xx = 0; xx <= FIELDX; xx++)
for (yy = 0; yy <= FIELDY; yy++)
if (field[xx][yy] == WOOD)
for (xxx = xx - 1; xxx <= xx + 1; xxx++)
for (yyy = yy - 1; yyy <= yy + 1; yyy++) if (valid(xxx, yyy) && field[xxx][yyy] == EMPTY)
field[xxx][yyy] = TEMPWOOD;
for (xx = 0; xx <= FIELDX; xx++)
for (yy = 0; yy <= FIELDY; yy++)
if (field[xx][yy] == TEMPWOOD)
change(xx, yy, WOOD);
} elif (c == CLOCK)
secondsperlevel -= rand() % CLOCKRAND;
} }
x = creature[which].x; /* We refresh these in case a fragment has been */
y = creature[which].y; /* reflected. Yes, it is vital. */
if (creature[which].alive && creature[which].visible && (creature[which].deltax || creature[which].deltay))
{ if (creature[which].species == MISSILE)
change(x, y, FIRSTMISSILE + creature[which].type);
elif (creature[which].species == DRIP)
change(x, y, FIRSTDRIP + creature[which].type);
elif (creature[which].species == ORB)
{ field[x][y] = ORB;
if (creature[which].mode == TONGUE)
draw(x, y, ORBTONGUE);
elif (creature[which].mode == ARMOUR)
draw(x, y, ORBARMOUR);
else draw(x, y, ORB);
} else /* fragments, goats, penguins, cyclones, dogs, clouds */
change(x, y, creature[which].species);
}
if (creature[which].alive)
{ /* decide whether to fire */
if (creature[which].species == GOAT)
{ if (!(rand() % 10))
{ for (i = 0; i <= CREATURES; i++)
{ if (!creature[i].alive)
{ creature[i].deltax = (rand() % 3) - 1;
creature[i].deltay = (rand() % 3) - 1;
if
( valid(x + creature[i].deltax, y + creature[i].deltay)
&& (creature[i].deltax || creature[i].deltay)
)
{ c = field[x + creature[i].deltax][y + creature[i].deltay];
if
( (c >= FIRSTEMPTY && c <= LASTEMPTY)
|| (c >= FIRSTTAIL && c <= LASTTAIL)
)
{ effect(FXGOATFIRE);
creature[i].alive = TRUE;
creature[i].species = FRAGMENT;
creature[i].x = x + creature[i].deltax;
creature[i].y = y + creature[i].deltay;
creature[i].visible = TRUE;
creature[i].last = EMPTY;
creature[i].speed = (SBYTE) atleast(FRAGSPEED - (level / 2), 1);
} }
break;
} } } }
elif (creature[which].species == CLOUD)
{ if (!(rand() % 30))
{ cloudbullet(which, x, y, -1);
cloudbullet(which, x, y, 1);
} } }
}
MODULE void cloudbullet(UBYTE which, SBYTE x, SBYTE y, SBYTE deltay)
{ UBYTE i, c;
for (i = 0; i <= CREATURES; i++)
{ if (!creature[i].alive)
{ creature[i].deltax = 0;
creature[i].deltay = deltay;
if (valid(x + creature[i].deltax, y + creature[i].deltay))
{ c = field[x + creature[i].deltax][y + creature[i].deltay];
if
( (c >= FIRSTEMPTY && c <= LASTEMPTY)
|| (c >= FIRSTTAIL && c <= LASTTAIL)
)
{ effect(FXGOATFIRE); /* this is used for clouds too */
creature[i].alive = TRUE;
creature[i].species = FRAGMENT;
creature[i].x = x;
creature[i].y = y + creature[i].deltay;
creature[i].visible = TRUE;
creature[i].last = EMPTY;
creature[i].speed = (SBYTE) atleast(FRAGSPEED - (level / 2), 1);
} }
break;
} } }
MODULE void orbsplit(SBYTE which)
{ SBYTE copy = 0, i;
effect(FXORBSPLIT);
for (i = 0; i <= CREATURES; i++)
{ if (!creature[i].alive)
{ creature[i].x = creature[which].x;
creature[i].y = creature[which].y;
creature[i].score = creature[which].score;
creature[i].armour = creature[which].armour;
creature[i].tongue = creature[which].tongue;
creature[i].mode = creature[which].mode;
creature[i].speed = creature[which].speed;
creature[i].explode = FALSE;
creature[i].multi = creature[which].multi;
creature[i].last = EMPTY;
switch (copy)
{
case 0:
if (creature[which].deltax != -1 || creature[which].deltay != -1)
{ creature[i].deltax = -1;
creature[i].deltay = -1;
creature[i].alive = TRUE;
}
break;
case 1:
if (creature[which].deltax != 1 || creature[which].deltay != 1)
{ creature[i].deltax = 1;
creature[i].deltay = 1;
creature[i].alive = TRUE;
}
break;
case 2:
if (creature[which].deltax != 1 || creature[which].deltay != -1)
{ creature[i].deltax = 1;
creature[i].deltay = -1;
creature[i].alive = TRUE;
}
break;
case 3:
if (creature[which].deltax != -1 || creature[which].deltay != 1)
{ creature[i].deltax = -1;
creature[i].deltay = 1;
creature[i].alive = TRUE;
}
break;
default:
break;
}
if (++copy >= 4)
return;
} } }
MODULE void orientworms(void)
{ SBYTE player;
for (player = 0; player <= 3; player++)
{ worm[player].speed = NORMAL;
if (worm[player].lives)
stat(player, NITRO);
worm[player].moved = FALSE;
worm[player].x = startx[level];
worm[player].y = starty[level];
switch (player)
{
case 0:
worm[0].deltax = -1;
worm[0].deltay = 0;
break;
case 1:
worm[1].deltax = 1;
worm[1].deltay = 0;
break;
case 2:
worm[2].deltax = 0;
worm[2].deltay = -1;
break;
case 3:
worm[3].deltax = 0;
worm[3].deltay = 1;
break;
default:
break;
} } }
SBYTE partner(SBYTE which)
{ if (which % 2 == 0)
return((SBYTE) (which + 1));
else return((SBYTE) (which - 1));
}
/* NAME putletter -- Put a letter onto the field
SYNOPSIS void putletter(SBYTE player);
INPUTS SBYTE player -
0-3: player on whose behalf the letter is put on for
-1: any letter
RESULT none */
MODULE void putletter(SBYTE player)
{ ABOOL done;
SBYTE i, x, y;
UBYTE letter;
do
{ done = findempty(&x, &y, FIRSTEMPTY, LASTEMPTY);
} while (!done);
if (player != -1)
{ for (i = 0; i <= LETTERS; i++)
if (!(letters[player][i]))
{ break;
}
if (i > LETTERS) /* if no spare letters */
letter = rand() % (LETTERS + 1);
else
{ do
letter = rand() % (LETTERS + 1);
while (letters[player][letter]);
} }
else letter = rand() % (LETTERS + 1);
change(x, y, letter + FIRSTLETTER);
letterx = x;
lettery = y;
lettertype = letter + FIRSTLETTER;
}
/* NAME queue -- adds a keystroke to the key queue
SYNOPSIS name(SBYTE, SBYTE, SBYTE);
FUNCTION Adds a keystroke to the in-game key queue.
INPUTS player - player that pressed the key
deltax - the deltax of the key
deltay - the deltay of the key
IMPLEMENTATION
thewormqueue[] array has WORMQUEUELIMIT as its last index.
It is implemented as a FIFO stack rather than LIFO so that
the keystrokes are processed in the correct order (that is,
the order in which they were pressed). The oldest keystroke
is always at index [0], the next oldest at [1], and so on
upwards to the newest keystroke, at [worm[player].pos].
Keystrokes are removed from the bottom of the array ([0]),
and the rest of the array is shuffled down to fill the gap,
so that the contents of [1] go to [0], the contents of [2]
go to [1], etc. worm[player].pos is adjusted to always point
to the newest entry, which is the 'end' of the queue.
MODULE engine.c */
AGLOBAL void wormqueue(SBYTE player, SBYTE deltax, SBYTE deltay)
{ if (worm[player].pos < WORMQUEUELIMIT)
{ worm[player].pos++;
thewormqueue[player][worm[player].pos].deltax = deltax;
thewormqueue[player][worm[player].pos].deltay = deltay;
} }
MODULE void dogqueue(SBYTE which, SBYTE deltax, SBYTE deltay)
{ if (creature[which].pos < DOGQUEUELIMIT)
{ creature[which].pos++;
thedogqueue[which][creature[which].pos].deltax = deltax;
thedogqueue[which][creature[which].pos].deltay = deltay;
} }
MODULE void reflect(UBYTE which)
{ creature[which].deltax = -creature[which].deltax;
creature[which].deltay = -creature[which].deltay;
creature[which].x += creature[which].deltax * 2;
creature[which].y += creature[which].deltay * 2;
}
ABOOL savefields(STRPTR fieldname)
{ SBYTE i, j;
TEXT IOBuffer[NAMELENGTH + 1];
matchteleports();
if (!ZOpen(fieldname, TRUE))
return FALSE;
/* write header
*/
strcpy(IOBuffer, "FSET 5.5");
IOBuffer[9] = levels;
if (!ZWrite(IOBuffer, 10))
{ ZClose();
return FALSE;
}
/* write high score table
*/
for (i = 0; i <= HISCORES; i++)
{ IOBuffer[0] = hiscore[i].player;
IOBuffer[1] = hiscore[i].level;
IOBuffer[2] = 0;
IOBuffer[3] = hiscore[i].score / 65536;
IOBuffer[4] = (hiscore[i].score % 65536) / 256;
IOBuffer[5] = (hiscore[i].score % 65536) % 256;
if (!ZWrite(IOBuffer, 6))
{ ZClose();
return FALSE;
}
for (j = 0; j <= NAMELENGTH; j++)
IOBuffer[j] = hiscore[i].name[j];
if (!ZWrite(IOBuffer, NAMELENGTH + 1))
{ ZClose();
return FALSE;
}
for (j = 0; j <= TIMELENGTH; j++)
IOBuffer[j] = hiscore[i].time[j];
if (!ZWrite(IOBuffer, TIMELENGTH + 1))
{ ZClose();
return FALSE;
}
for (j = 0; j <= DATELENGTH; j++)
IOBuffer[j] = hiscore[i].date[j];
if (!ZWrite(IOBuffer, DATELENGTH + 1))
{ ZClose();
return FALSE;
} }
/* write level data
*/
for (i = 0; i <= levels; i++)
{ IOBuffer[0] = startx[i];
IOBuffer[1] = starty[i];
IOBuffer[2] = teleport[i][0].alive;
IOBuffer[3] = teleport[i][0].x;
IOBuffer[4] = teleport[i][0].y;
IOBuffer[5] = teleport[i][1].alive;
IOBuffer[6] = teleport[i][1].x;
IOBuffer[7] = teleport[i][1].y;
if (!ZWrite(IOBuffer, 8))
{ ZClose();
return FALSE;
}
if (!ZWrite((char *) &board[i][0][0], (FIELDX + 1) * (FIELDY + 1)))
{ ZClose();
return FALSE;
} }
/* write version string
*/
if (!ZWrite(VERSION, strlen(VERSION)))
{ ZClose();
return FALSE;
}
ZClose();
if (clearthem)
clearhiscores();
modified = FALSE;
return TRUE;
}
void saylevel(COLOUR colour)
{ TEXT mainstring[15] = "Level ", tempstring[4];
if (level > 0)
{ stci_d(&mainstring[6], level);
strcat((char*) mainstring, " of ");
stci_d(tempstring, levels);
strcat((char*) mainstring, (char*) tempstring);
say(mainstring, colour);
} else
{ if (a == FIELDEDIT)
say("Bonus Level", colour);
else
{ if (leveltype == TREASURE)
say("Bonus Level: Treasury!", colour);
elif (leveltype == DRIP)
say("Bonus Level: Drips!", colour);
else
{ /* assert(leveltype == CLOUD);
*/
say("Bonus Level: Clouds!", colour);
} } } }
MODULE SBYTE slowdown(SBYTE speed)
{ speed *= 2;
if (speed > SLOW)
speed = SLOW;
return(speed);
}
MODULE void slowloop(void)
{ SBYTE i, player, which, x, xx, y, yy;
UBYTE thissy;
/* decrement worm strength */
for (player = 0; player <= 3; player++)
if (worm[player].lives > 0 && ice == -1 || ice == player)
{ if (worm[player].bias > 0)
{ worm[player].bias--;
stat(player, BIAS);
}
if (worm[player].ice > 0 && --worm[player].ice == 0)
{ for (which = 0; which <= 3; which++)
if (player != which)
worm[player].pos = -1;
ice = -1;
}
if (worm[player].mode == ARMOUR)
{ if (--worm[player].armour == 0)
{ if (worm[player].tongue > 0)
worm[player].mode = TONGUE;
else worm[player].mode = NULL;
}
stat(player, ARMOUR);
} elif (worm[player].mode == TONGUE)
{ if (--worm[player].tongue == 0)
{ if (worm[player].armour > 0)
worm[player].mode = ARMOUR;
else worm[player].mode = NULL;
}
stat(player, TONGUE);
} }
/* blank out old causes */
for (player = 0; player <= 3; player++)
{ if (worm[player].lives > 0 && r > worm[player].causewait)
{ drawcause(player, BLACK);
worm[player].causewait = (ULONG) -1; /* most future time possible */
} }
if (level)
{ /* decrement orb strength */
for (which = 0; which <= CREATURES; which++)
if (creature[which].alive && creature[which].species == ORB)
if (creature[which].mode == ARMOUR)
{ if (--creature[which].armour == 0)
if (creature[which].tongue > 0)
creature[which].mode = TONGUE;
else creature[which].mode = NULL;
} elif (creature[which].mode == TONGUE)
{ if (--creature[which].tongue == 0)
if (creature[which].armour > 0)
creature[which].mode = ARMOUR;
else creature[which].mode = NULL;
}
/* create goats */
if
( (!(rand() % freq))
&& findempty(&x, &y, FIRSTGOAT, LASTGOAT)
&& field[x][y] != GOAT
)
{ for (i = 0; i <= CREATURES; i++)
{ if (!creature[i].alive)
{ effect(FXGOATBORN);
creature[i].x = x;
creature[i].y = y;
creature[i].alive = TRUE;
creature[i].species = GOAT;
creature[i].last = creature[i].oldlast = field[x][y];
creature[i].speed = (SBYTE) atleast(GOATSPEED - (level / 2), 2);
creature[i].visible = TRUE;
change(x, y, GOAT);
break;
} } }
/* create orbs */
if
( (!(rand() % freq))
&& findempty(&x, &y, FIRSTEMPTY, LASTEMPTY)
)
{ for (i = 0; i <= CREATURES; i++)
{ if (!creature[i].alive)
{ effect(FXORBBORN);
creature[i].deltax = (rand() % 2) * 2 - 1;
creature[i].deltay = (rand() % 2) * 2 - 1;
creature[i].score = 0;
creature[i].alive = TRUE;
creature[i].armour = 0;
creature[i].tongue = 0;
creature[i].mode = NULL;
creature[i].speed = (SBYTE) atleast(ORBSPEED - (level / 2), 2);
creature[i].species = ORB;
creature[i].explode = FALSE;
creature[i].multi = 1;
creature[i].x = x;
creature[i].y = y;
creature[i].last = EMPTY;
creature[i].visible = TRUE;
change(x, y, ORB);
break;
} } }
/* create penguins */
if
( (!(rand() % freq))
&& findempty(&x, &y, FIRSTEMPTY, LASTEMPTY))
{ for (i = 0; i <= CREATURES; i++)
if (!(creature[i].alive))
{ effect(FXPENGUINBORN);
creature[i].x = x;
creature[i].y = y;
creature[i].last = EMPTY;
creature[i].speed = (SBYTE) atleast(PENGUINSPEED - (level / 2), 2);
creature[i].visible = TRUE;
creature[i].deltax = 0;
creature[i].deltay = 0;
creature[i].species = PENGUIN;
creature[i].alive = TRUE;
change(x, y, PENGUIN);
break;
} }
/* create dogs */
if ((!(rand() % freq)) && findempty(&x, &y, FIRSTEMPTY, LASTEMPTY))
{ for (i = 0; i <= CREATURES; i++)
if (!(creature[i].alive))
{ creature[i].x = x;
creature[i].y = y;
creature[i].last = EMPTY;
creature[i].speed = (SBYTE) atleast(DOGSPEED - (level / 2), 2);
creature[i].visible = TRUE;
creature[i].deltax = 0;
creature[i].deltay = 0;
creature[i].species = DOG;
creature[i].alive = TRUE;
creature[i].pos = -1;
creature[i].dormant = DORMANT; /* dormant */
field[x][y] = DOG;
draw(x, y, DOGDORMANT);
break;
} }
/* create slime */
if ((!(rand() % (freq * 2))) && findempty(&x, &y, FIRSTEMPTY, LASTEMPTY))
change(x, y, SLIME);
/* grow slime */
if (!(rand() % (freq * 2)))
{ for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (field[x][y] == SLIME)
for (xx = x - 1; xx <= x + 1; xx++)
for (yy = y - 1; yy <= y + 1; yy++)
if (valid(xx, yy) && field[xx][yy] == EMPTY && !(rand() % 2))
field[xx][yy] = TEMPSLIME;
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (field[x][y] == TEMPSLIME)
change(x, y, SLIME);
}
/* create timebombs
*/
if ((!(rand() % (freq * 8))) && findempty(&x, &y, FIRSTEMPTY, LASTEMPTY))
{ for (i = 0; i <= CREATURES; i++)
if (!(creature[i].alive))
{ creature[i].x = x;
creature[i].y = y;
creature[i].last = EMPTY;
creature[i].speed = (SBYTE) atleast(TIMEBOMBSPEED - (level / 2), 2);
creature[i].visible = TRUE;
creature[i].deltax = 0;
creature[i].deltay = 0;
creature[i].species = TIMEBOMB;
creature[i].alive = TRUE;
creature[i].time = 10;
field[x][y] = TIMEBOMB;
draw(x, y, ZERO + 9);
break;
} } }
/* create drips */
if
( (level && (!(rand() % freq)))
|| ((!level) && leveltype == DRIP && (!(rand() % FREQ_DRIP)))
)
{ x = (rand() % (FIELDX + 1));
y = (rand() % 3);
thissy = field[x][y];
if (thissy >= FIRSTEMPTY && thissy <= LASTEMPTY)
{ for (i = 0; i <= CREATURES; i++)
{ if (!creature[i].alive)
{ effect(FXDRIPBORN);
creature[i].alive = TRUE;
creature[i].last = EMPTY;
creature[i].species = DRIP;
creature[i].type = rand() % 4;
creature[i].x = x;
creature[i].y = y;
creature[i].deltax = 0;
creature[i].deltay = 1;
creature[i].visible = TRUE;
if (level)
creature[i].speed = (SBYTE) atleast(DRIPSPEED - (level / 2), 2);
else creature[i].speed = BONUSDRIPSPEED;
change(x, y, FIRSTDRIP + creature[which].type);
break;
} } } }
/* create clouds */
if
( (level && (!(rand() % freq)))
|| ((!level) && leveltype == CLOUD && (!(rand() % FREQ_CLOUD)))
)
{ x = (rand() % (FIELDX - 1)) + 1;
y = (rand() % (FIELDY - 1)) + 1;
/* If you create clouds at an extreme horizontal position
(0 or FIELDX), will cause a crash because the deltas will be
wrong. */
thissy = field[x][y];
if (thissy >= FIRSTEMPTY && thissy <= LASTEMPTY)
{ for (i = 0; i <= CREATURES; i++)
{ if (!creature[i].alive)
{ creature[i].alive = TRUE;
creature[i].last = EMPTY;
creature[i].species = CLOUD;
creature[i].x = x;
creature[i].y = y;
if (x < FIELDX / 2)
creature[i].deltax = 1;
else creature[i].deltax = -1;
creature[i].deltay = 0;
creature[i].visible = TRUE;
if (level)
creature[i].speed = (SBYTE) atleast(CLOUDSPEED - (level / 2), 3);
else creature[i].speed = BONUSCLOUDSPEED;
change(x, y, CLOUD);
break;
} } } }
/* create objects
*/
for (which = 0; which <= LASTOBJECT; which++)
if (level || leveltype != TREASURE || which == TREASURE)
{ if (!(rand() % object[which].freq) && findempty(&x, &y, FIRSTEMPTY, LASTEMPTY))
change(x, y, which);
} elif (!(rand() % (object[which].freq / 10)) && findempty(&x, &y, FIRSTEMPTY, LASTEMPTY))
change(x, y, which);
/* create teleports
*/
if (!(rand() % FREQ_TELEPORT)
&& !teleport[level][2].alive
&& findempty(&(teleport[level][2].x), &(teleport[level][2].y), FIRSTEMPTY, LASTEMPTY)
&& findempty(&(teleport[level][3].x), &(teleport[level][3].y), FIRSTEMPTY, LASTEMPTY)
&& (teleport[level][2].x != teleport[level][3].x || teleport[level][2].y != teleport[level][3].y))
{ teleport[level][2].alive = TRUE;
teleport[level][3].alive = TRUE;
change(teleport[level][2].x, teleport[level][2].y, TELEPORT);
change(teleport[level][3].x, teleport[level][3].y, TELEPORT);
} }
MODULE SBYTE speedup(SBYTE speed, ABOOL nitro)
{ speed /= 2;
if (speed < FAST)
speed = FAST;
return(speed);
}
MODULE ULONG squareblast(SBYTE type, SBYTE player, SBYTE thissy, SBYTE x, SBYTE y)
{ SBYTE which;
ULONG score = 0;
SBYTE filler;
if (type == HEAD && worm[player].bias)
filler = SILVER;
else filler = EMPTY;
if (thissy <= LASTOBJECT)
change(x, y, filler);
elif (thissy >= FIRSTTAIL && thissy <= LASTTAIL)
change(x, y, filler);
elif (thissy == ORB)
{ which = whichcreature(x, y, ORB, 255);
if (creature[which].mode != ARMOUR)
{ effect(FXORBDEATH);
creature[which].alive = FALSE;
score = creature[which].score;
if (type == HEAD && worm[player].bias)
worm[player].lives += ORBBLOOD;
change(x, y, BONUS);
} else effect(FXUSEARMOUR);
} elif (thissy >= FIRSTDRIP && thissy <= LASTDRIP)
{ creature[whichcreature(x, y, DRIP, 255)].alive = FALSE;
change(x, y, filler);
} elif (thissy >= FIRSTHEAD && thissy <= LASTHEAD)
{ if (type != HEAD || player != thissy - FIRSTHEAD)
if (worm[thissy - FIRSTHEAD].mode != ARMOUR)
{ worm[thissy - FIRSTHEAD].cause = BOMB;
worm[thissy - FIRSTHEAD].alive = FALSE;
stat(thissy - FIRSTHEAD, LIFE);
if (type == HEAD)
worm[thissy - FIRSTHEAD].victor = player;
else
{ worm[thissy - FIRSTHEAD].victor = -1;
score = KILLWORM; // worms will get this bonus from death(), so it is not given for them here.
} }
else effect(FXUSEARMOUR);
} elif (thissy >= FIRSTMISSILE && thissy <= LASTMISSILE)
{ if (type != HEAD || player != thissy - FIRSTMISSILE)
{ creature[whichcreature(x, y, MISSILE, 255)].alive = FALSE;
change(x, y, filler);
} }
elif (thissy == PENGUIN)
{ creature[whichcreature(x, y, PENGUIN, 255)].alive = FALSE;
change(x, y, filler);
} elif (thissy == FRAGMENT)
{ creature[whichcreature(x, y, FRAGMENT, 255)].alive = FALSE;
change(x, y, filler);
} elif (thissy == GOAT)
{ creature[whichcreature(x, y, GOAT, 255)].alive = FALSE;
change(x, y, BONUS);
}
return(score);
}
void timeloop(void)
{ TEXT timedisplay[5] = {"#:##"};
PERSIST ABOOL outoftime = FALSE;
UBYTE i;
secondsleft = atleast(secondsleft, 0);
timedisplay[0] = 48 + (secondsleft / 60);
timedisplay[2] = 48 + ((secondsleft % 60) / 10);
timedisplay[3] = 48 + ((secondsleft % 60) % 10);
if (!level)
{ say(timedisplay, worm[treasurer].colour);
if (!secondsleft)
{ level = reallevel + 1;
secondsleft = SECONDSPERLEVEL;
newlevel(treasurer);
} }
elif (!secondsleft)
{ if (!outoftime)
{ effect(FXTIMEALERT);
say("Out of time!", WHITE);
outoftime = TRUE;
freq /= 2;
for (i = 0; i <= CREATURES; i++)
if (creature[i].alive)
creature[i].speed = speedup(creature[i].speed, TRUE);
} }
else
{ outoftime = FALSE;
say(timedisplay, WHITE);
} }
void train(SCANCODE scancode)
{ SBYTE i, x, y;
UBYTE thissy;
switch(scancode) {
case HELP:
trainer = !trainer;
break;
case NUMERICSLASH:
/* Complete the level.
*/
if (trainer)
{ trainer = FALSE;
if (worm[1].lives > 0)
for (i = 0; i <= LETTERS; i++)
{ letters[1][i] = TRUE;
drawletter(1, FIRSTLETTER + i, NORMAL);
} }
break;
case NUMERICASTERISK:
/* Change most squares to gold.
*/
if (trainer)
{ trainer = FALSE;
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (field[x][y] == FIRSTTAIL + 1)
change(x, y, METAL);
else
{ thissy = field[x][y];
if
( thissy != STONE
&& thissy != GOAT
&& thissy != ORB
&& thissy != SKULL
&& (thissy < FIRSTHEAD || thissy > LASTHEAD)
&& (thissy < FIRSTLETTER || thissy > LASTLETTER)
)
change(x, y, GOLD);
} }
break;
case NUMERICPLUS:
/* Full lives, tongue, bias, ammo, power, nitro, affixer
and remnants. */
if (trainer)
{ trainer = FALSE;
if (worm[1].lives > 0)
{ worm[1].lives = LIVESLIMIT;
stat(1, LIFE);
worm[1].tongue = MODELIMIT;
worm[1].mode = TONGUE;
stat(1, TONGUE);
worm[1].bias = BIASLIMIT;
stat(1, BIAS);
worm[1].ammo = AMMOLIMIT;
stat(1, AMMO);
worm[1].power = POWERLIMIT;
stat(1, POWER);
worm[1].nitro = TRUE;
stat(1, NITRO);
worm[1].affixer = TRUE;
worm[1].remnants = TRUE;
worm[1].sideshot = TRUE;
trainer = FALSE;
} }
break;
default:
break;
} }
MODULE void turnworm(SBYTE player, SBYTE deltax, SBYTE deltay)
{
if (worm[player].nitro || !deltax || !deltay)
{ if (worm[player].deltax == deltax && worm[player].deltay == deltay)
{ worm[player].speed = speedup(worm[player].speed, worm[player].nitro);
stat(player, NITRO);
} elif (worm[player].deltax == -deltax && worm[player].deltay == -deltay)
{ worm[player].speed = slowdown(worm[player].speed);
stat(player, NITRO);
} else
{ worm[player].deltax = deltax;
worm[player].deltay = deltay;
} }
}
AGLOBAL void updatesquare(SBYTE x, SBYTE y)
{ SBYTE which;
if (startx[level] == x && starty[level] == y)
draw(x, y, START);
elif (board[level][x][y] == TELEPORT)
{ for (which = 0; which <= 1; which++)
if (teleport[level][which].alive && teleport[level][which].x == x && teleport[level][which].y == y)
draw(x, y, ONE + which);
} else draw(x, y, board[level][x][y]);
}
SBYTE valid(SBYTE x, SBYTE y)
{ if (x >= 0 && x <= FIELDX && y >= 0 && y <= FIELDY)
return(TRUE);
else
return(FALSE);
}
MODULE UBYTE whichcreature(SBYTE x, SBYTE y, UBYTE species, UBYTE exception)
{ UBYTE i;
for (i = 0; i <= CREATURES; i++)
if
( creature[i].alive
&& creature[i].x == x
&& creature[i].y == y
&& creature[i].species == species
&& i != exception
)
return i;
return 255; /* error code
*/
}
MODULE SBYTE whichteleport(SBYTE x, SBYTE y)
{ SBYTE which;
for (which = 0; which <= 3; which++)
if (teleport[level][which].alive && teleport[level][which].x == x && teleport[level][which].y == y)
return((SBYTE) which);
return((SBYTE) -1); /* error code
*/
}
MODULE void wormbullet(SBYTE player)
{ ABOOL finished,
flag = FALSE,
ok = FALSE,
lettered = FALSE;
LONG score;
SBYTE distance,
thissy,
i, j,
x, y;
if (!worm[player].ammo)
{ stat(player, BONUS);
if (worm[player].speed == FAST)
distance = FASTDISTANCE;
elif (worm[player].speed == NORMAL)
distance = NORMALDISTANCE;
else
{ /* assert(worm[player].speed == SLOW); */
distance = SLOWDISTANCE;
}
/* check for metal */
for (i = 1; i < distance; i++)
{ x = xwrap(worm[player].x + (i * worm[player].deltax));
y = ywrap(worm[player].y + (i * worm[player].deltay));
if (field[x][y] == METAL)
flag = TRUE;
}
if (!flag)
{ /* assert(abs(worm[player].deltax) == 1 && abs(worm[player].deltay) == 1); */
x = xwrap(worm[player].x + worm[player].deltax * distance);
y = ywrap(worm[player].y + worm[player].deltay * distance);
thissy = field[x][y];
if (thissy == TELEPORT)
{ i = whichteleport(x, y);
if (!blocked(i, worm[player].deltax, worm[player].deltay))
ok = TRUE;
}
if (ok || ((thissy < STONE || thissy > GOAT) && thissy != METAL))
{ effect(FXJUMP);
worm[player].deltax *= distance;
worm[player].deltay *= distance;
} } }
else
{ effect(FXSHOOT);
worm[player].ammo--;
stat(player, AMMO);
if (worm[player].bias)
createmissile(player);
if (worm[player].sideshot)
{ bullet[7].alive = bullet[8].alive = TRUE;
bullet[7].teleported = bullet[8].teleported = 0;
bullet[7].visible = bullet[8].visible = TRUE;
bullet[7].reflected = bullet[8].reflected = FALSE;
bullet[7].x = bullet[8].x = worm[player].x;
bullet[7].y = bullet[8].y = worm[player].y;
if (!worm[player].deltax && worm[player].deltay)
{ bullet[7].deltax = -1;
bullet[8].deltax = 1;
bullet[7].deltay = bullet[8].deltay = 0;
} elif (worm[player].deltax && !worm[player].deltay)
{ bullet[7].deltax = bullet[8].deltax = 0;
bullet[7].deltay = -1;
bullet[8].deltay = 1;
} else /* worm is diagonal */
{ if (worm[player].deltax == worm[player].deltay)
{ bullet[7].deltax = 1;
bullet[7].deltay = -1;
} else
{ bullet[7].deltax = -1;
bullet[7].deltay = -1;
}
bullet[8].deltax = -bullet[7].deltax;
bullet[8].deltay = -bullet[7].deltay;
} }
for (i = 0; i <= worm[player].power; i++)
{ bullet[i].alive = TRUE;
bullet[i].teleported = 0;
bullet[i].visible = TRUE;
bullet[i].reflected = FALSE;
bullet[i].deltax = worm[player].deltax;
bullet[i].deltay = worm[player].deltay;
if (i % 2 == 0)
distance = i / 2;
else distance = -((i + 1) / 2);
if (worm[player].deltax == 0)
{ bullet[i].x = worm[player].x + distance;
bullet[i].y = worm[player].y;
} elif (worm[player].deltay == 0)
{ bullet[i].x = worm[player].x;
bullet[i].y = worm[player].y + distance;
} else
{ switch (i)
{
case 0:
bullet[i].x = worm[player].x + worm[player].deltax;
bullet[i].y = worm[player].y + worm[player].deltay;
break;
case 1:
bullet[i].x = worm[player].x + worm[player].deltax;
bullet[i].y = worm[player].y;
break;
case 2:
bullet[i].x = worm[player].x;
bullet[i].y = worm[player].y + worm[player].deltay;
break;
case 3:
bullet[i].x = worm[player].x + worm[player].deltax * 2;
bullet[i].y = worm[player].y;
break;
case 4:
bullet[i].x = worm[player].x;
bullet[i].y = worm[player].y + worm[player].deltay * 2;
break;
case 5:
bullet[i].x = worm[player].x + worm[player].deltax * 2;
bullet[i].y = worm[player].y - worm[player].deltay;
break;
case 6:
bullet[i].x = worm[player].x - worm[player].deltax;
bullet[i].y = worm[player].y + worm[player].deltay * 2;
break;
default:
break;
} } }
/* Bullets are now set up. */
score = 0;
finished = FALSE;
while (!finished)
{ finished = TRUE;
for (i = 0; i <= 8; i++)
{ if (bullet[i].alive)
{ finished = FALSE;
if (bullet[i].reflected)
{ bullet[i].x -= bullet[i].deltax;
bullet[i].y -= bullet[i].deltay;
} else
{ bullet[i].x += bullet[i].deltax;
bullet[i].y += bullet[i].deltay;
}
x = bullet[i].x;
y = bullet[i].y;
thissy = field[x][y];
if (!(valid(x, y)))
bullet[i].alive = FALSE;
elif (x == worm[player].x && y == worm[player].y)
{ /* hit by own bullet */
bullet[i].alive = FALSE;
if (worm[player].mode != ARMOUR)
{ worm[player].cause = FIRSTFIRE + player;
worm[player].victor = -1;
worm[player].alive = FALSE;
} }
elif (thissy >= FIRSTHEAD && thissy <= LASTHEAD)
{ if (worm[thissy - FIRSTHEAD].mode != ARMOUR)
{ worm[thissy - FIRSTHEAD].cause = FIRSTFIRE + player;
worm[thissy - FIRSTHEAD].victor = player;
worm[thissy - FIRSTHEAD].alive = FALSE;
if (player != thissy - FIRSTHEAD)
score += KILLWORM + HITSHOT;
} else effect(FXUSEARMOUR);
bullet[i].alive = FALSE;
} elif (thissy >= FIRSTPROTECTOR && thissy <= LASTPROTECTOR)
{ if (player != thissy - FIRSTPROTECTOR)
{ effect(FXUSEPROTECTOR);
bullet[i].alive = FALSE;
} else bullet[i].visible = FALSE;
} elif (thissy >= FIRSTMISSILE && thissy <= LASTMISSILE)
{ if (player != thissy - FIRSTMISSILE)
creature[whichcreature(x, y, MISSILE, 255)].alive = FALSE;
else bullet[i].visible = FALSE;
} elif (thissy >= FIRSTDRIP && thissy <= LASTDRIP)
{ j = whichcreature(x, y, DRIP, 255);
creature[j].alive = FALSE;
} elif (thissy >= FIRSTLETTER && thissy <= LASTLETTER)
{ wormletter(player, thissy);
lettered = TRUE;
} else
{ switch(thissy)
{
case METAL:
if (bullet[i].reflected)
bullet[i].alive = FALSE;
else
{ bullet[i].reflected = TRUE;
bullet[i].x -= bullet[i].deltax * 2;
bullet[i].y -= bullet[i].deltay * 2;
}
break;
case STONE:
case WHIRLWIND:
bullet[i].alive = FALSE;
break;
case ORB:
bullet[i].alive = FALSE;
j = whichcreature(x, y, ORB, 255);
if (creature[j].mode != ARMOUR)
{ creature[j].explode = TRUE;
score += creature[j].score + HITSHOT;
if (worm[player].bias)
worm[player].lives += ORBBLOOD;
} else effect(FXUSEARMOUR);
break;
case TELEPORT:
j = whichteleport(bullet[i].x, bullet[i].y);
if (bullet[i].teleported == 2)
bullet[i].alive = FALSE;
else
{ effect(FXUSETELEPORT);
bullet[i].visible = FALSE;
bullet[i].teleported++;
bullet[i].x = teleport[level][partner(j)].x;
bullet[i].y = teleport[level][partner(j)].y;
score += TELPOINT;
}
break;
case WOOD:
if (!worm[player].bias)
bullet[i].alive = FALSE;
break;
case GOAT:
creature[whichcreature(x, y, GOAT, 255)].alive = FALSE;
change(x, y, BONUS);
score += KILLGOAT + HITSHOT;
if (worm[player].bias)
worm[player].lives += GOATBLOOD;
bullet[i].alive = FALSE;
break;
case SKULL:
bullet[i].alive = FALSE;
break;
case FRAGMENT:
bullet[i].alive = FALSE;
creature[whichcreature(x, y, FRAGMENT, 255)].alive = FALSE;
change(x, y, EMPTY);
break;
case PENGUIN:
bullet[i].alive = FALSE;
creature[whichcreature(x, y, PENGUIN, 255)].alive = FALSE;
change(x, y, EMPTY);
score += KILLPENGUIN + HITSHOT;
if (worm[player].bias)
worm[player].lives += PENGUINBLOOD;
break;
case DOG:
bullet[i].alive = FALSE;
break;
case CLOUD:
creature[whichcreature(x, y, CLOUD, 255)].alive = FALSE;
default:
break;
} }
// x and y need this refreshing here
x = bullet[i].x;
y = bullet[i].y;
if (bullet[i].alive && bullet[i].visible)
{ draw(x, y, FIRSTFIRE + player);
if (worm[player].remnants)
field[x][y] = FIRSTFIRE + player;
elif (bullet[i].teleported)
change(x, y, GOLD);
else change(x, y, EMPTY);
} } } }
if (lettered)
putletter(player);
wormscore(player, score);
if (worm[player].bias)
stat(player, LIFE);
clearkybd();
} }
MODULE void wormletter(SBYTE player, SBYTE thissy)
{ if (thissy == GREEN_C)
effect(FX_C);
elif (thissy == RED_O)
effect(FX_O);
elif (thissy == BLUE_M)
effect(FX_M);
elif (thissy == YELLOW_P)
effect(FX_P);
elif (thissy == GREEN_L)
effect(FX_L);
elif (thissy == BLUE_T)
effect(FX_T);
else
{ /* assert(thissy == RED_E || thissy == YELLOW_E); */
effect(FX_E);
}
letters[player][thissy - FIRSTLETTER] = TRUE;
drawletter(player, thissy, NORMAL);
wormscore(player, LETTERPOINT);
}
/* NAME wormloop -- controls worms and protectors
SYNOPSIS wormloop(SBYTE);
FUNCTION Amiga-worm thinking, processing one keystroke from the
worm's queue, all the worm's protectors' control, the
worm's control.
MODULE engine.c */
MODULE void wormloop(SBYTE player)
{ AUTO ABOOL enclosed = FALSE, ok;
AUTO SBYTE animal = HEAD, bestgood, bestx, besty, c, d, dirx, diry,
good, i, iwhich, thisprot = 0, which, x, y, leftx,
rightx, topy, bottomy;
AUTO UBYTE counter;
AUTO ULONG score = 0;
PERSIST SBYTE deltas[4][2][19] = // direction, x/y, counter
{ { { 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 1, 1, 1, 1}, // northwest
{ -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0}
},
{ { 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, -1, -1, -1, -1}, // northeast
{ -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0}
},
{ { 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, -1, -1, -1, -1}, // southeast
{ 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, 0, 0, 0, 0}
},
{ { 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 1, 1, 1, 1}, // southwest
{ 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, 0, 0, 0, 0}
} };
PERSIST struct
{ SBYTE leftx, rightx, topy, bottomy;
} enclose[4] =
{ { -5, -1, -5, -1 }, // northwest
{ 1, 5, -5, -1 }, // northeast
{ 1, 5, 1, 5 }, // southeast
{ -5, -1, 1, 5 } // southwest
};
/* AI: Amiga worm control
Worm checks ahead, left and right one square. Assigns opinions to those
three choices and then takes the appropriate one. */
if (worm[player].control == AMIGA)
{ if (!(rand() % 50))
wormqueue(player, (rand() % 3) - 1, (rand() % 3) - 1);
else
{ bestgood = -128;
for (i = 0; i <= 2; i++)
{ switch(i) {
case 0:
dirx = worm[player].deltax;
diry = worm[player].deltay;
break;
case 1:
if (worm[player].deltax == 0) /* if going north or south */
{ dirx = -1; /* then look west */
diry = 0;
} else /* if going east or west */
{ dirx = 0; /* then look north */
diry = -1;
}
break;
case 2:
if (worm[player].deltax == 0) /* if going north or south */
{ dirx = 1; /* then look east */
diry = 0;
} else /* if going east or west */
{ dirx = 0; /* then look south */
diry = 1;
}
break;
default:
break;
}
c = field[xwrap(worm[player].x + dirx)][ywrap(worm[player].y + diry)];
if (c >= FIRSTLETTER && c <= LASTLETTER)
good = 100;
elif (c <= LASTOBJECT)
good = (SBYTE) object[c].score;
elif (c == FIRSTPROTECTOR + player)
good = 6;
elif (c == FIRSTFIRE + player)
good = 5;
elif (c >= FIRSTTAIL && c <= LASTTAIL)
{ if (player == c - FIRSTTAIL)
good = -30;
else good = -60;
} else switch(c) {
case SKULL:
good = 70;
break;
case GOLD:
good = 50;
break;
case SILVER:
good = 40;
break;
case EMPTY:
good = 10;
break;
case WOOD:
good = -80;
break;
case STONE:
good = -90;
break;
case METAL:
good = -90;
break;
case GOAT:
good = -95;
break;
case WHIRLWIND:
good = -100;
break;
default:
good = -50; /* slime, heads, orbs, etc. */
break;
}
if (good > bestgood)
{ bestx = dirx;
besty = diry;
bestgood = good;
} }
if (bestgood <= -60)
wormqueue(player, 0, 0);
elif (bestgood < 0 && (!(rand() % 2)))
wormqueue(player, 0, 0);
elif (bestx != worm[player].deltax || besty != worm[player].deltay)
wormqueue(player, bestx, besty);
} }
/* remove a keystroke from the worm queue */
if (worm[player].pos != -1)
{ if (thewormqueue[player][0].deltax == 0 && thewormqueue[player][0].deltay == 0)
wormbullet(player);
else turnworm(player, thewormqueue[player][0].deltax, thewormqueue[player][0].deltay);
if (--worm[player].pos != -1)
for (i = 0; i <= worm[player].pos; i++)
{ thewormqueue[player][i].deltax = thewormqueue[player][i + 1].deltax;
thewormqueue[player][i].deltay = thewormqueue[player][i + 1].deltay;
} }
/* move worm */
change(worm[player].x, worm[player].y, worm[player].last);
worm[player].x = xwrap(worm[player].x + worm[player].deltax);
worm[player].y = ywrap(worm[player].y + worm[player].deltay);
worm[player].last = FIRSTTAIL + player;
for (i = 0; i <= CREATURES; i++)
{ if (creature[i].alive && creature[i].species == DOG && creature[i].dormant > DORMANT && creature[i].type == player)
{ dogqueue(i, worm[player].deltax, worm[player].deltay);
if (creature[i].dormant < CHASING)
{ creature[i].dormant++;
draw(creature[i].x, creature[i].y, DOGAWAKENING);
} } }
/* The deltas are not changed back to the range of -1..1 until after the
dogs have looked at the queue. This enables them to jump. */
worm[player].deltax = bsign(worm[player].deltax);
worm[player].deltay = bsign(worm[player].deltay);
// check for enclosure
for (i = 0; i <= 3; i++)
{ x = worm[player].x;
y = worm[player].y;
ok = TRUE;
for (counter = 0; counter <= 18; counter++)
{ x = xwrap(x + deltas[i][0][counter]);
y = ywrap(y + deltas[i][1][counter]);
if (field[x][y] != FIRSTTAIL + player)
{ ok = FALSE;
break;
} }
if (ok)
{ effect(FXENCLOSURE);
enclosed = TRUE;
leftx = xwrap(worm[player].x + enclose[i].leftx);
rightx = xwrap(worm[player].x + enclose[i].rightx);
topy = ywrap(worm[player].y + enclose[i].topy);
bottomy = ywrap(worm[player].y + enclose[i].bottomy);
for (x = leftx; x <= rightx; x = xwrap(x + 1))
for (y = topy; y <= bottomy; y = ywrap(y + 1))
{ d = field[x][y];
if ((d >= FIRSTEMPTY && d <= LASTEMPTY) || (d >= FIRSTTAIL && d <= LASTTAIL && player != d - FIRSTTAIL))
{ change(x, y, FIRSTTAIL + player);
score += ENCLOSUREPOINT;
} } } }
// move protectors
for (which = 0; which <= PROTECTORS; which++)
{ if (protector[player][which].alive)
{ if (protector[player][which].visible)
change(protector[player][which].x, protector[player][which].y, protector[player][which].last);
else protector[player][which].visible = TRUE;
protector[player][which].last = EMPTY;
if (which == NOSE)
{ protector[player][which].relx = worm[player].deltax * NOSEDISTANCE;
protector[player][which].rely = worm[player].deltay * NOSEDISTANCE;
if (!worm[player].affixer)
{ worm[player].position = 0;
if (worm[player].deltax == 0)
protector[player][which].relx = worm[player].position;
elif (worm[player].deltay == 0)
protector[player][which].rely = worm[player].position;
elif (worm[player].position == -1)
protector[player][which].relx = worm[player].deltax * (NOSEDISTANCE - 1);
elif (worm[player].position == 1)
protector[player][which].rely = worm[player].deltay * (NOSEDISTANCE - 1);
} }
elif (!worm[player].affixer)
{ if (protector[player][which].relx == 1 && protector[player][which].rely == -1)
{ protector[player][which].deltax = 0;
protector[player][which].deltay = 1;
} elif (protector[player][which].relx == 1 && protector[player][which].rely == 1)
{ protector[player][which].deltax = -1;
protector[player][which].deltay = 0;
} elif (protector[player][which].relx == -1 && protector[player][which].rely == 1)
{ protector[player][which].deltax = 0;
protector[player][which].deltay = -1;
} elif (protector[player][which].relx == -1 && protector[player][which].rely == -1)
{ protector[player][which].deltax = 1;
protector[player][which].deltay = 0;
}
protector[player][which].relx += protector[player][which].deltax;
protector[player][which].rely += protector[player][which].deltay;
}
protector[player][which].x = worm[player].x + protector[player][which].relx;
protector[player][which].y = worm[player].y + protector[player][which].rely;
if (!valid(protector[player][which].x, protector[player][which].y))
protector[player][which].visible = FALSE;
} }
/* collision detection */
while (animal != NOSQUARE)
{ if (animal == HEAD)
{ x = worm[player].x;
y = worm[player].y;
c = field[x][y];
if (c >= FIRSTHEAD && c <= LASTHEAD)
wormworm(x, y, player, c - FIRSTHEAD);
elif (c >= FIRSTTAIL && c <= LASTTAIL)
{ if (worm[player].mode == TONGUE)
{ effect(FXUSETONGUE);
if (players > 1)
if (player == c - FIRSTTAIL)
{ score += TURNTOSILVER;
worm[player].last = SILVER;
} else
{ score += TURNTOGOLD;
worm[player].last = GOLD;
} }
elif (!enclosed)
{ worm[player].cause = c;
worm[player].alive = FALSE;
worm[player].victor = c - FIRSTTAIL;
} }
elif (c == TIMEBOMB)
{ /* push timebomb */
which = whichcreature(x, y, TIMEBOMB, 255);
if (valid(x + worm[player].deltax, y + worm[player].deltay))
{ d = field[x + worm[player].deltax][y + worm[player].deltay];
if (d == TELEPORT)
score += BOMBOVEREDGE;
elif (d <= LASTEMPTY)
{ creature[which].x += worm[player].deltax;
creature[which].y += worm[player].deltay;
field[creature[which].x][creature[which].y] = TIMEBOMB;
draw(creature[which].x, creature[which].y, ZERO + creature[which].time);
} else
{ if (worm[player].mode == NULL)
draw(worm[player].x, worm[player].y, eachworm[player][0][worm[player].deltax + 1 + (worm[player].deltay + 1) * 3]);
else draw(worm[player].x, worm[player].y, eachworm[player][1][worm[player].deltax + 1 + (worm[player].deltay + 1) * 3]);
bombblast(HEAD, player, worm[player].x, worm[player].y);
creature[which].alive = FALSE;
} }
else score += BOMBOVEREDGE;
} elif (c >= FIRSTPROTECTOR && c <= LASTPROTECTOR)
protworm(x, y, c - FIRSTPROTECTOR, player);
elif (c >= FIRSTMISSILE && c <= LASTMISSILE)
{ i = whichcreature(x, y, MISSILE, 255);
wormmissile(x, y, player, i);
} elif (c >= FIRSTDRIP && c <= LASTDRIP)
{ i = whichcreature(x, y, DRIP, 255);
wormdrip(x, y, player, i);
} elif (c == STONE || c == GOAT || c == METAL)
{ worm[player].cause = c;
worm[player].victor = -1;
worm[player].alive = FALSE;
ramming(player);
} elif (c == WOOD)
{ if (worm[player].mode != ARMOUR)
{ worm[player].cause = WOOD;
worm[player].alive = FALSE;
worm[player].victor = -1;
} }
elif (c == SLIME)
{ if (worm[player].mode != ARMOUR)
{ worm[player].cause = SLIME;
worm[player].alive = FALSE;
worm[player].victor = -1;
} }
elif (c == PENGUIN)
{ i = whichcreature(x, y, PENGUIN, 255);
wormpenguin(x, y, player, i);
} elif (c == WHIRLWIND)
{ i = whichcreature(x, y, WHIRLWIND, 255);
wormwhirlwind(x, y, player, i);
} elif (c == DOG)
{ i = whichcreature(x, y, DOG, 255);
wormdog(x, y, player, i);
} elif (c == ORB)
{ i = whichcreature(x, y, ORB, 255);
wormorb(x, y, player, i);
} elif (c == FRAGMENT)
{ i = whichcreature(x, y, FRAGMENT, 255);
wormfrag(x, y, player, i);
} elif (c == CLOUD)
{ i = whichcreature(x, y, CLOUD, 255);
cloudworm(x, y, i, player);
} elif (c == TELEPORT)
{ which = whichteleport(x, y);
if (blocked(which, worm[player].deltax, worm[player].deltay))
{ worm[player].cause = TELEPORT;
worm[player].victor = -1;
worm[player].alive = FALSE;
ramming(player);
} else
{ effect(FXUSETELEPORT);
score += TELPOINT;
worm[player].x = xwrap(teleport[level][partner(which)].x + worm[player].deltax);
worm[player].y = ywrap(teleport[level][partner(which)].y + worm[player].deltay);
} }
elif (c >= FIRSTFIRE && c <= LASTFIRE)
{ if (player != c - FIRSTFIRE && worm[player].mode != ARMOUR)
{ worm[player].cause = REMNANTS;
worm[player].victor = c - FIRSTFIRE;
worm[player].alive = FALSE;
} } }
else
{ /* assert(animal == PROTECTOR); */
x = protector[player][thisprot].x;
y = protector[player][thisprot].y;
c = field[x][y];
if (c >= FIRSTHEAD && c <= LASTHEAD)
protworm(x, y, player, c - FIRSTHEAD);
elif (c >= FIRSTTAIL && c <= LASTTAIL)
{ if (player == c - FIRSTTAIL || worm[player].mode == TONGUE)
protector[player][thisprot].visible = FALSE;
} elif (c >= FIRSTPROTECTOR && c <= LASTPROTECTOR)
protprot(x, y, player, c - FIRSTPROTECTOR);
elif (c == STONE || c == WOOD || c == METAL || c == TIMEBOMB || c == TELEPORT)
protector[player][thisprot].visible = FALSE;
elif (c >= FIRSTMISSILE && c <= LASTMISSILE)
{ i = whichcreature(x, y, MISSILE, 255);
protmissile(x, y, player, i);
} elif (c == WHIRLWIND)
{ i = whichcreature(x, y, WHIRLWIND, 255);
protwhirlwind(x, y, player, i);
} elif (c == DOG)
{ i = whichcreature(x, y, DOG, 255);
protdog(x, y, player, i);
} elif (c >= FIRSTDRIP && c <= LASTDRIP)
{ i = whichcreature(x, y, DRIP, 255);
protdrip(x, y, player, i);
} elif (c == PENGUIN)
{ i = whichcreature(x, y, PENGUIN, 255);
protpenguin(x, y, player, i);
} elif (c == ORB)
{ i = whichcreature(x, y, ORB, 255);
protorb(x, y, player, i);
} elif (c == FRAGMENT)
{ i = whichcreature(x, y, FRAGMENT, 255);
protfrag(x, y, player, i);
} elif (c == CLOUD)
{ i = whichcreature(x, y, CLOUD, 255);
cloudprot(x, y, i, player);
} elif (c == GOAT)
{ effect(FXUSEPROTECTOR);
effect(FXGOATDEATH);
creature[whichcreature(x, y, GOAT, 255)].alive = FALSE;
protector[player][thisprot].last = BONUS;
score += KILLGOAT;
if (worm[player].bias)
{ worm[player].lives += GOATBLOOD;
stat(player, LIFE);
} } }
/* The next collisions apply equally to worms and protectors. */
if (c >= FIRSTLETTER && c <= LASTLETTER)
{ wormletter(player, c);
putletter(player);
} elif (c <= LASTOBJECT)
score += wormobject(player, x, y);
else
{ switch(c)
{
case EMPTY:
score += EMPTYPOINT;
break;
case SILVER:
score += SILVERPOINT;
break;
case GOLD:
score += GOLDPOINT;
break;
case SKULL:
effect(FXGETSKULL);
score += SKULLPOINT;
for (which = 0; which <= 3; which++)
{ if (worm[which].alive == FALSE && worm[which].x == worm[player].x && worm[which].y == worm[player].y)
iwhich = which;
}
worm[player].bias += worm[iwhich].bias;
if (worm[player].bias > 0)
{ stat(player, BIAS);
worm[iwhich].bias = 0;
stat(iwhich, BIAS);
}
worm[player].multi *= worm[iwhich].multi;
if (worm[player].multi > 1)
{ if (worm[player].multi > MULTILIMIT)
worm[player].multi = MULTILIMIT;
}
worm[player].power += worm[iwhich].power;
if (worm[player].power > 1)
{ if (worm[player].power > POWERLIMIT)
worm[player].power = POWERLIMIT;
stat(player, POWER);
worm[iwhich].power = 0;
stat(iwhich, POWER);
}
worm[player].ammo += worm[iwhich].ammo;
if (worm[player].ammo > 0)
{ stat(player, AMMO);
worm[iwhich].ammo = 0;
stat(iwhich, AMMO);
}
worm[player].armour += worm[iwhich].armour;
if (worm[player].armour > 0)
{ stat(player, ARMOUR);
worm[iwhich].armour = 0;
stat(iwhich, ARMOUR);
}
worm[player].tongue += worm[iwhich].tongue;
if (worm[player].tongue > 0)
{ stat(player, TONGUE);
worm[iwhich].tongue = 0;
stat(iwhich, TONGUE);
}
if (worm[player].armour > 0 || worm[player].tongue > 0)
{ if (worm[player].armour >= worm[player].tongue)
worm[player].mode = ARMOUR;
else worm[player].mode = TONGUE;
}
if (worm[iwhich].nitro)
{ worm[player].nitro = TRUE;
stat(player, NITRO);
worm[iwhich].nitro = FALSE;
worm[iwhich].speed = NORMAL;
stat(iwhich, NITRO);
}
if (worm[iwhich].affixer)
worm[player].affixer = TRUE;
if (worm[iwhich].remnants)
worm[player].remnants = TRUE;
if (worm[iwhich].sideshot)
worm[player].sideshot = TRUE;
for (which = 0; which <= LETTERS; which++)
if (letters[iwhich][which])
{ drawletter(iwhich, FIRSTLETTER + which, BLACK);
if (!letters[player][which])
{ letters[player][which] = TRUE;
drawletter(player, FIRSTLETTER + which, NORMAL);
} }
break;
default:
break;
} }
if (animal == HEAD)
/* it is important that HEAD is done before PROTECTORs */
{ field[worm[player].x][worm[player].y] = FIRSTHEAD + player;
if (worm[player].alive)
{ switch (worm[player].mode)
{
case NULL:
draw(worm[player].x, worm[player].y, eachworm[player][0][worm[player].deltax + 1 + (worm[player].deltay + 1) * 3]);
break;
case TONGUE:
if (worm[player].tongue < 10 && (r / SLOW) % 2 == 0)
draw(worm[player].x, worm[player].y, WHITENED);
else draw(worm[player].x, worm[player].y, eachworm[player][1][worm[player].deltax + 1 + (worm[player].deltay + 1) * 3]);
break;
case ARMOUR:
if (worm[player].armour < 10 && (r / SLOW) % 2 == 0)
draw(worm[player].x, worm[player].y, WHITENED);
else draw(worm[player].x, worm[player].y, eachworm[player][1][worm[player].deltax + 1 + (worm[player].deltay + 1) * 3]);
break;
default:
break;
} }
else draw(worm[player].x, worm[player].y, SKULL);
animal = PROTECTOR;
} elif (animal == PROTECTOR)
{ if (protector[player][thisprot].alive && protector[player][thisprot].visible)
change(x, y, FIRSTPROTECTOR + player);
do
{ thisprot++;
} while ((!protector[player][thisprot].alive || !valid(protector[player][thisprot].x, protector[player][thisprot].y)) && thisprot <= PROTECTORS);
if (thisprot > PROTECTORS)
animal = NOSQUARE;
} }
wormscore(player, score);
}
void wormscore(SBYTE player, LONG score)
{ worm[player].score += score * worm[player].multi * players;
stat(player, BONUS);
}
MODULE SWORD wsign(SWORD value)
{ if (value < 0)
return (-1);
elif (value > 0)
return (1);
else
return (0);
}
AGLOBAL SBYTE xwrap(SBYTE x)
{ if (x < 0)
x += FIELDX + 1;
elif (x > FIELDX)
x -= FIELDX + 1;
return(x);
}
AGLOBAL SBYTE ywrap(SBYTE y)
{ if (y < 0)
y += FIELDY + 1;
elif (y > FIELDY)
y -= FIELDY + 1;
return(y);
}
MODULE void ramming(SBYTE player)
{ SBYTE i;
worm[player].x = xwrap(worm[player].x - worm[player].deltax);
worm[player].y = ywrap(worm[player].y - worm[player].deltay);
for (i = 0; i <= PROTECTORS; i++)
{ /* no point checking whether the protectors are alive or dead
*/
protector[player][i].x -= worm[player].deltax;
protector[player][i].y -= worm[player].deltay;
} }
MODULE SWORD atleast(SWORD value, SWORD minimum)
{ if (value < minimum)
return(minimum);
else return(value);
}
MODULE void orbscore(SBYTE which, ULONG score)
{ creature[which].score += score * creature[which].multi;
}
MODULE void __inline change(SBYTE x, SBYTE y, UBYTE image)
{ field[x][y] = image;
draw(x, y, image);
}
MODULE void createmissile(UBYTE player)
{ UBYTE i;
for (i = 0; i <= CREATURES; i++)
if (!creature[i].alive)
{ effect(FXMISSILEACTIVE);
creature[i].alive = TRUE;
creature[i].x = worm[player].x;
creature[i].y = worm[player].y;
creature[i].species = MISSILE;
creature[i].type = player;
creature[i].last = EMPTY;
creature[i].visible = FALSE;
if (level)
creature[i].speed = (SBYTE) atleast(MISSILESPEED - (level / 2), 1);
else creature[i].speed = BONUSMISSILESPEED;
break;
} }
/* WormWars FSET 5.6 format for fieldset contents and high
score table (Amiga and IBM-PC), as follows:
header
TEXT[] "FSET 5.6" (NULL-terminated)
SBYTE levels;
high score table
for (slot = 0; slot <= HISCORES; slot++)
{ SBYTE hiscore[slot].player,
hiscore[slot].level;
SLONG hiscore[slot].score;
TEXT[] hiscore[slot].name (NULL-terminated)
TEXT[] hiscore[slot].time (NULL-terminated)
TEXT[] hiscore[slot].date (NULL-terminated)
}
level data
for (level = 0; level <= levels; level++)
{ SBYTE startx[level],
starty[level];
ABOOL teleport[level][0].alive;
SBYTE teleport[level][0].x,
teleport[level][0].y;
ABOOL teleport[level][1].alive;
SBYTE teleport[level][1].x,
teleport[level][1].y;
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
SBYTE board[level][x][y];
}
version string
TEXT[] "$VER: Worm Wars 5.61 (dd.mm.yy) $" (NULL-terminated) */
MODULE SBYTE onlyworm(ABOOL alive)
{ UBYTE i, theworm, worms = 0;
for (i = 0; i <= 3; i++)
if (worm[i].control != NONE && ((!alive) || worm[i].lives))
{ theworm = i;
worms++;
}
if (worms == 1)
return (SBYTE) theworm;
else return -1;
}
/* cloud, prot, worm, dog, drip, frag, missile, orb, penguin, whirlwind
Whichever is earlier in that list comes earlier in the function name. For
example, frag-orb and orb-frag collisions both use fragorb(): there is no
such routine as orbfrag(). Also, there are no such routines as dripdrip()
and penguinpenguin(). */
MODULE void dogdog(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
creature[which2].alive = FALSE;
change(x, y, LIFE);
}
MODULE void dogdrip(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
}
MODULE void dogfrag(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
creature[which2].alive = FALSE;
change(x, y, BONUS);
}
MODULE void dogmissile(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
creature[which2].alive = FALSE;
change(x, y, BONUS);
}
MODULE void dogorb(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
}
MODULE void dogpenguin(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which2].alive = FALSE;
}
MODULE void dogwhirlwind(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
}
MODULE void dripfrag(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
creature[which2].alive = FALSE;
change(x, y, BONUS);
}
MODULE void dripmissile(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ /* drip lives, missile dies
*/
creature[which2].alive = FALSE;
}
MODULE void driporb(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
if (creature[which2].mode != ARMOUR)
{ creature[which2].explode = TRUE;
draw(x, y, EMPTY);
} }
MODULE void drippenguin(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which2].alive = FALSE;
}
MODULE void dripwhirlwind(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
}
MODULE void fragfrag(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ effect(FXTHUD);
creature[which1].alive = FALSE;
creature[which2].alive = FALSE;
draw(x, y, EMPTY);
}
MODULE void fragmissile(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
creature[which2].alive = FALSE;
change(x, y, EMPTY);
}
MODULE void fragorb(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
if (creature[which2].mode != ARMOUR)
{ creature[which2].explode = TRUE;
draw(x, y, EMPTY);
} else effect(FXUSEARMOUR);
}
MODULE void fragpenguin(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which2].alive = FALSE;
}
MODULE void fragwhirlwind(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
}
MODULE void missilemissile(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
creature[which2].alive = FALSE;
change(x, y, BONUS);
}
MODULE void missilepenguin(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which2].alive = FALSE;
}
MODULE void missilewhirlwind(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
}
MODULE void orborb(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ effect(FXORBDEATH);
creature[which1].alive = FALSE;
creature[which2].alive = FALSE;
change(x, y, BONUS);
}
MODULE void orbmissile(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ if (creature[which1].mode != ARMOUR)
{ effect(FXORBDEATH);
creature[which1].alive = FALSE;
creature[which2].alive = FALSE;
change(x, y, BONUS);
wormscore(creature[which2].type, creature[which1].score);
if (worm[creature[which2].type].bias)
{ worm[creature[which2].type].lives += ORBBLOOD;
stat(creature[which2].type, LIFE);
} }
else effect(FXUSEARMOUR);
}
MODULE void orbpenguin(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which2].alive = FALSE;
if (creature[which1].mode == ARMOUR)
orbscore(which1, KILLPENGUIN);
else
{ creature[which1].alive = FALSE;
change(x, y, BONUS);
} }
MODULE void orbwhirlwind(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
}
MODULE void penguinwhirlwind(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
}
MODULE void whirlwindwhirlwind(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
creature[which2].alive = FALSE;
change(x, y, LIFE);
}
MODULE void wormdog(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ if (creature[which2].dormant == DORMANT)
{ effect(FXDOGAWAKENS);
creature[which2].dormant = AWAKENING;
creature[which2].type = which1;
worm[which1].last = DOG;
} else
{ creature[which2].alive = FALSE;
if (worm[which1].mode != ARMOUR)
{ worm[which1].alive = FALSE;
worm[which1].cause = DOG;
worm[which1].victor = -1;
} } }
MODULE void wormdrip(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which2].alive = FALSE;
if (which1 == creature[which2].type)
{ effect(FXDRIP);
wormscore(which1, DRIPBONUS);
} else
{ worm[which1].alive = FALSE;
worm[which1].cause = FIRSTDRIP + creature[which2].type;
worm[which1].victor = -1;
} }
MODULE void wormfrag(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ if (worm[which1].mode != ARMOUR)
{ worm[which1].cause = FRAGMENT;
worm[which1].victor = -1;
worm[which1].alive = FALSE;
creature[which2].alive = FALSE;
} else
{ effect(FXUSEARMOUR);
reflect(which2);
} }
MODULE void wormmissile(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ if (creature[which2].type == which1)
creature[which2].visible = FALSE;
else
{ creature[which2].alive = FALSE;
if (worm[which1].mode != ARMOUR)
{ worm[which1].cause = FIRSTMISSILE + creature[which2].type;
worm[which1].victor = creature[which2].type;
worm[which1].alive = FALSE;
} else effect(FXUSEARMOUR);
} }
MODULE void wormorb(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ if (worm[which1].mode == ARMOUR)
{ effect(FXUSEARMOUR);
effect(FXORBDEATH);
wormscore(which1, creature[which2].score);
creature[which2].alive = FALSE;
if (worm[which1].bias)
{ worm[which1].lives += ORBBLOOD;
stat(which1, LIFE);
} }
else
{ if (creature[which2].mode == ARMOUR)
{ effect(FXUSEARMOUR);
orbscore(which2, KILLWORM);
} else creature[which2].alive = FALSE;
worm[which1].cause = ORB;
worm[which1].victor = -1;
worm[which1].alive = FALSE;
} }
MODULE void wormpenguin(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ effect(FXPENGUINDEATH);
creature[which2].alive = FALSE;
if (worm[which1].armour)
{ wormscore(which1, KILLPENGUIN);
if (worm[which1].bias)
{ worm[which1].lives += PENGUINBLOOD;
stat(which1, LIFE);
} }
else
{ worm[which1].alive = FALSE;
worm[which1].cause = PENGUIN;
worm[which1].victor = -1;
} }
MODULE void wormwhirlwind(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ worm[which1].cause = WHIRLWIND;
worm[which1].victor = -1;
worm[which1].alive = FALSE;
}
MODULE void wormworm(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ if (worm[which1].mode != TONGUE && worm[which2].mode != TONGUE)
{ /* both worms die
*/
worm[which1].cause = FIRSTHEAD + which2;
worm[which1].alive = FALSE;
worm[which1].victor = -1;
worm[which2].cause = FIRSTHEAD + which1;
worm[which2].alive = FALSE;
worm[which2].victor = -1;
} elif (worm[which1].mode == TONGUE && worm[which2].mode != TONGUE)
{ /* 1st worm lives, 2nd worm dies
*/
worm[which2].cause = FIRSTHEAD + which1;
worm[which2].alive = FALSE;
worm[which2].victor = which1;
} elif (worm[which1].mode != TONGUE && worm[which2].mode == TONGUE)
{ /* 1st worm dies, 2nd worm lives
*/
worm[which1].cause = FIRSTHEAD + which2;
worm[which1].alive = FALSE;
worm[which1].victor = which2;
} }
MODULE void protdog(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which2].alive = FALSE;
}
MODULE void protdrip(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which2].alive = FALSE;
if (which1 == creature[which2].type)
{ effect(FXDRIP);
wormscore(which1, DRIPBONUS);
} else effect(FXUSEPROTECTOR);
}
MODULE void protfrag(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ effect(FXUSEPROTECTOR);
reflect(which2);
}
MODULE void protmissile(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ if (which1 != creature[which2].type)
{ effect(FXUSEPROTECTOR);
creature[which2].alive = FALSE;
} else creature[which2].visible = FALSE;
}
MODULE void protorb(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ effect(FXUSEPROTECTOR);
effect(FXORBDEATH);
wormscore(which1, creature[which2].score);
creature[which2].alive = FALSE;
if (worm[which1].bias)
{ worm[which1].lives += ORBBLOOD;
stat(which1, LIFE);
} }
MODULE void protpenguin(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ effect(FXUSEPROTECTOR);
effect(FXPENGUINDEATH);
wormscore(which1, KILLPENGUIN);
creature[which2].alive = FALSE;
if (worm[which1].bias)
{ worm[which1].lives += PENGUINBLOOD;
stat(which1, LIFE);
} }
MODULE void protwhirlwind(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ UBYTE i;
for (i = 0; i <= PROTECTORS; i++)
if (protector[which1][i].alive && protector[which1][i].x == x && protector[which1][i].y == y)
protector[which1][i].alive = FALSE;
}
MODULE void protworm(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ UBYTE i, j;
for (i = 0; i <= PROTECTORS; i++)
if (protector[which1][i].alive && protector[which1][i].x == x && protector[which1][i].y == y)
{ j = i;
break;
}
if (which1 != which2)
{ if (worm[which2].mode != ARMOUR)
{ effect(FXUSEPROTECTOR);
worm[which2].cause = FIRSTPROTECTOR + which1;
worm[which2].victor = which1;
worm[which2].alive = FALSE;
} else
{ effect(FXUSEARMOUR);
protector[which1][j].visible = FALSE;
} }
else
{ /* protector is over worm's own head; caused by ramming */
protector[which1][j].visible = FALSE;
} }
MODULE void protprot(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ UBYTE i, p1, p2;
/* Find both protectors */
for (i = 0; i <= PROTECTORS; i++)
{ if (protector[which1][i].alive && protector[which1][i].x == x && protector[which1][i].y == y)
p1 = i;
if (protector[which2][i].alive && protector[which2][i].x == x && protector[which2][i].y == y)
p2 = i;
}
protector[which1][p1].alive = FALSE;
protector[which2][p2].alive = FALSE;
change(x, y, EMPTY);
}
MODULE void cloudcloud(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
creature[which2].alive = FALSE;
change(x, y, LIGHTNING);
}
MODULE void clouddog(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which2].alive = FALSE;
}
MODULE void clouddrip(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which2].alive = FALSE;
}
MODULE void cloudfrag(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
}
MODULE void cloudmissile(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which2].alive = FALSE;
}
MODULE void cloudorb(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
}
MODULE void cloudpenguin(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which2].alive = FALSE;
}
MODULE void cloudprot(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
}
MODULE void cloudwhirlwind(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
}
MODULE void cloudworm(SBYTE x, SBYTE y, UBYTE which1, UBYTE which2)
{ creature[which1].alive = FALSE;
if (worm[which2].mode != ARMOUR)
{ worm[which2].alive = FALSE;
worm[which2].cause = CLOUD;
worm[which2].victor = -1;
} }
MODULE void magnetloop(void)
{ SBYTE i;
UBYTE c;
for (i = 0; i <= MAGNETS; i++)
if (magnet[i].alive)
{ // quick check to make sure this magnet is still valid
if (field[magnet[i].x][magnet[i].y] != magnet[i].object)
magnet[i].alive = FALSE;
else
{ change(magnet[i].x, magnet[i].y, EMPTY);
magnet[i].x += bsign(worm[magnet[i].player].x - magnet[i].x);
magnet[i].y += bsign(worm[magnet[i].player].y - magnet[i].y);
c = field[magnet[i].x][magnet[i].y];
if ((c >= FIRSTEMPTY && c <= LASTEMPTY)
|| (c >= FIRSTTAIL && c <= LASTTAIL)
|| (c >= FIRSTHEAD && c <= LASTHEAD))
{ change(magnet[i].x, magnet[i].y, magnet[i].object);
if (c >= FIRSTHEAD && c <= LASTHEAD)
{ wormscore(c - FIRSTHEAD, wormobject(c - FIRSTHEAD, magnet[i].x, magnet[i].y));
change(magnet[i].x, magnet[i].y, FIRSTHEAD + magnet[i].player); // not entirely the right head image
} }
else magnet[i].alive = FALSE;
} } }
MODULE ULONG wormobject(UBYTE player, SBYTE x, SBYTE y)
{ AUTO UBYTE c = field[x][y], d;
AUTO ULONG score = object[c].score;
AUTO UBYTE i, j;
AUTO SBYTE xx, xxx, yy, yyy;
AUTO ABOOL done;
PERSIST SBYTE otherfield[FIELDX + 1][FIELDY + 1];
for (i = 0; i <= MAGNETS; i++)
if (magnet[i].alive && x == magnet[i].x && y == magnet[i].y)
magnet[i].alive = FALSE;
if (c != SLAYER && c != BOMB && c != MISSILE && c != NITRO && c != POWER && c != AMMO)
effect(FXGETOBJECT);
switch(c)
{
case BONUS:
i = rand() % (LETTERS + 1);
letters[player][i] = TRUE;
drawletter(player, FIRSTLETTER + i, NORMAL);
break;
case AMMO:
effect(FXGETAMMO);
worm[player].ammo += (rand() % 5) + 2; /* 2-6 bullets */
stat(player, AMMO);
break;
case ARMOUR:
worm[player].armour += MODEADD + (rand() % MODERAND);
worm[player].mode = ARMOUR;
stat(player, ARMOUR);
break;
case TONGUE:
worm[player].tongue += MODEADD + (rand() % MODERAND);
worm[player].mode = TONGUE;
stat(player, TONGUE);
worm[player].last = FIRSTTAIL + player;
break;
case NITRO:
effect(FXGETNITRO);
worm[player].nitro = TRUE;
stat(player, NITRO);
break;
case BOMB:
if (worm[player].mode == NULL)
draw(worm[player].x, worm[player].y, eachworm[player][0][worm[player].deltax + 1 + (worm[player].deltay + 1) * 3]);
else draw(worm[player].x, worm[player].y, eachworm[player][1][worm[player].deltax + 1 + (worm[player].deltay + 1) * 3]);
bombblast(HEAD, player, worm[player].x, worm[player].y);
break;
case POWER:
effect(FXGETPOWERUP);
if (worm[player].power < POWERLIMIT)
{ worm[player].power += 2;
stat(player, POWER);
}
break;
case SLAYER:
for (i = 0; i <= CREATURES; i++)
if (creature[i].alive)
{ if (creature[i].species == ORB)
{ effect(FXORBDEATH);
score += creature[i].score;
creature[i].explode = TRUE;
if (worm[player].bias)
worm[player].lives += ORBBLOOD;
} elif (creature[i].species == GOAT)
{ effect(FXGOATDEATH);
creature[i].alive = FALSE;
score += KILLGOAT;
if (worm[player].bias)
worm[player].lives += GOATBLOOD;
change(creature[i].x, creature[i].y, BONUS);
} elif (creature[i].species == DRIP)
{ creature[i].alive = FALSE;
change(creature[i].x, creature[i].y, EMPTY);
} elif (creature[i].species == MISSILE && creature[i].type != player)
{ creature[i].alive = FALSE;
change(creature[i].x, creature[i].y, EMPTY);
} }
for (i = 0; i <= 3; i++)
if (player != i && worm[i].mode != ARMOUR)
{ worm[i].alive = FALSE;
worm[i].cause = SLAYER;
worm[i].victor = player;
}
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (field[x][y] == SLIME)
change(x, y, EMPTY);
if (worm[player].bias)
stat(player, LIFE);
break;
case PROTECTOR:
done = FALSE;
for (i = 0; i <= PROTECTORS; i++)
if (!protector[player][i].alive && !done)
{ do
{ protector[player][i].relx = ((rand() % 2) * 2) - 1;
protector[player][i].rely = ((rand() % 2) * 2) - 1;
for (j = 0; j <= PROTECTORS; j++)
if (i == NOSE || !protector[player][j].alive || protector[player][j].x != xwrap(worm[player].x + protector[player][i].relx) || protector[player][j].y != ywrap(worm[player].y + protector[player][i].rely))
{ effect(FXPROTECTORBORN);
done = TRUE;
protector[player][i].alive = TRUE;
protector[player][i].visible = FALSE;
protector[player][i].last = EMPTY;
if (i == NOSE)
worm[player].position = -1;
}
} while (!done);
}
break;
case MISSILE:
createmissile(player);
break;
case LIFE:
worm[player].lives += (rand() % 5) + 2; /* 2-6 lives */
stat(player, LIFE);
break;
case MULTIPLIER:
if (worm[player].multi < MULTILIMIT)
worm[player].multi *= 2;
break;
case BIAS:
worm[player].bias += MODEADD + (rand() % MODERAND);
stat(player, BIAS);
break;
case ICE:
worm[player].ice += ICEADD + (rand() % ICERAND);
ice = player;
break;
case GROWER:
effect(FXGETGROWER);
/* grow silver */
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (field[x][y] == SILVER)
for (xx = x - 1; xx <= x + 1; xx++)
for (yy = y - 1; yy <= y + 1; yy++)
if (valid(xx, yy))
if (field[xx][yy] == EMPTY || field[xx][yy] == TEMPTAIL)
field[xx][yy] = TEMPSILVER;
/* grow gold */
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (field[x][y] == GOLD)
for (xx = x - 1; xx <= x + 1; xx++)
for (yy = y - 1; yy <= y + 1; yy++)
if (valid(xx, yy))
if (field[xx][yy] == EMPTY || field[xx][yy] == TEMPTAIL || field[xx][yy] == TEMPSILVER)
field[xx][yy] = TEMPGOLD;
/* update field */
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
switch (field[x][y])
{
case TEMPGOLD:
change(x, y, GOLD);
break;
case TEMPSILVER:
change(x, y, SILVER);
break;
case TEMPTAIL:
change(x, y, FIRSTTAIL + player);
break;
default:
break;
}
break;
case TREASURE:
treasurer = player;
if (level)
{ secondsperlevel = 0;
leveltype = rand() % 3;
if (leveltype == 0)
{ say("Bonus Level: Treasury!", worm[treasurer].colour);
leveltype = TREASURE;
} elif (leveltype == 1)
{ say("Bonus Level: Drips!", worm[treasurer].colour);
leveltype = DRIP;
} else
{ /* assert(leveltype == 2); */
say("Bonus Level: Clouds!", worm[treasurer].colour);
leveltype = CLOUD;
} }
secondsperlevel += TREASUREADD + (rand() % TREASURERAND);
if (level && leveltype != TREASURE)
secondsperlevel *= 2;
if (level)
{ stat(player, BONUS);
reallevel = level;
level = 0;
newlevel(player);
}
break;
case AFFIXER:
worm[player].affixer = TRUE;
break;
case SWITCHER:
if (players >= 2)
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (field[x][y] >= FIRSTTAIL && field[x][y] <= LASTTAIL && field[x][y] != FIRSTTAIL + player)
change(x, y, FIRSTTAIL + player);
elif (worm[player].bias && field[x][y] >= FIRSTFIRE && field[x][y] <= LASTFIRE && field[x][y] != FIRSTFIRE + player)
change(x, y, FIRSTFIRE + player);
break;
case HEALER:
if (worm[player].lives < 100)
worm[player].lives = 100;
else worm[player].lives = LIVESLIMIT;
stat(player, LIFE);
break;
case UMBRELLA:
level += (rand() % 2) + 1;
if (level >= levels)
level = level - 1;
for (i = 0; i <= LETTERS; i++)
letters[player][i] = TRUE;
break;
case CLOCK:
if (secondsleft)
secondsperlevel += (rand() % CLOCKRAND) + CLOCKADD;
break;
case SLOWER:
for (i = 0; i <= CREATURES; i++)
if (creature[i].alive && creature[i].species != MISSILE)
creature[i].speed = (SBYTE) atleast(creature[i].speed * 2, VERYFAST);
break;
case PULSE:
explosion(xwrap(worm[player].x - worm[player].deltax),
ywrap(worm[player].y - worm[player].deltay),
worm[player].deltax,
worm[player].deltay
);
break;
case REMNANTS:
worm[player].remnants = TRUE;
break;
case SIDESHOT:
worm[player].sideshot = TRUE;
break;
case MAGNET:
i = 0;
field[x][y] = EMPTY; // so that the magnet itself is destroyed
for (xx = 0; xx <= FIELDX; xx++)
for (yy = 0; yy <= FIELDY; yy++)
if (field[xx][yy] <= LASTOBJECT)
{ while (magnet[i].alive && i < MAGNETS)
i++;
if (i > MAGNETS)
{ break;
} else
{ magnet[i].x = xx;
magnet[i].y = yy;
magnet[i].object = field[xx][yy];
magnet[i].player = player;
magnet[i].alive = TRUE;
i++;
} }
break;
case CYCLONE:
/* create cyclones */
for (i = 0; i <= CREATURES; i++)
if (!(creature[i].alive))
{ effect(FXCYCLONE);
done = FALSE;
while (!done)
{ creature[i].x = rand() % (FIELDX + 1);
creature[i].y = (rand() % (FIELDY - 2)) + 3;
d = field[creature[i].x][creature[i].y];
if ((d >= FIRSTEMPTY && d <= LASTEMPTY) || (d >= FIRSTTAIL && d <= LASTTAIL))
done = TRUE;
/* Theoretically, this loop could hang... */
}
creature[i].last = EMPTY;
creature[i].speed = VERYFAST;
creature[i].visible = TRUE;
creature[i].deltax = 0;
creature[i].deltay = 0;
creature[i].species = WHIRLWIND;
creature[i].alive = TRUE;
change(creature[i].x, creature[i].y, WHIRLWIND);
break;
}
break;
case LIGHTNING:
for (xx = 0; xx <= FIELDX; xx++)
for (yy = 0; yy <= FIELDY; yy++)
otherfield[xx][yy] = EMPTY;
for (xx = 0; xx <= FIELDX; xx++)
for (yy = 0; yy <= FIELDY; yy++)
if (field[xx][yy] == FIRSTTAIL + player)
for (xxx = xx - 1; xxx <= xx + 1; xxx++)
for (yyy = yy - 1; yyy <= yy + 1; yyy++)
if (valid(xxx, yyy))
{ d = field[xxx][yyy];
if (d == ORB
|| d == GOAT
|| d == MISSILE
|| d == PENGUIN
|| d == FRAGMENT
|| (d >= FIRSTTAIL && d <= LASTTAIL && d != FIRSTTAIL + player)
|| (d >= FIRSTHEAD && d <= LASTHEAD)
|| (d >= FIRSTDRIP && d <= LASTDRIP)
|| d <= LASTOBJECT)
{ otherfield[xxx][yyy] = TEMPLIGHTNING;
draw(xxx, yyy, LIGHTNING);
} }
for (xx = 0; xx <= FIELDX; xx++)
{ for (yy = 0; yy <= FIELDY; yy++)
{ if (otherfield[xx][yy] == TEMPLIGHTNING)
{ d = field[xx][yy];
switch(d)
{
case ORB:
i = whichcreature(xx, yy, ORB, 255);
if (creature[i].mode == ARMOUR)
draw(xx, yy, ORBARMOUR);
else
{ creature[i].alive = FALSE;
score += creature[i].score;
change(xx, yy, BONUS);
}
break;
case GOAT:
creature[whichcreature(xx, yy, GOAT, 255)].alive = FALSE;
score += KILLGOAT;
change(xx, yy, BONUS);
break;
case MISSILE:
i = whichcreature(xx, yy, MISSILE, 255);
if (player != creature[i].type)
{ creature[i].alive = FALSE;
change(xx, yy, EMPTY);
} else draw(xx, yy, FIRSTMISSILE + player);
break;
case PENGUIN:
score += KILLPENGUIN;
// note no break here
case FRAGMENT:
creature[whichcreature(xx, yy, d, 255)].alive = FALSE;
change(xx, yy, EMPTY);
break;
default:
if (d >= FIRSTDRIP && d <= LASTDRIP)
{ creature[whichcreature(xx, yy, DRIP, 255)].alive = FALSE;
score += DRIPBONUS;
} elif (d >= FIRSTHEAD && d <= LASTHEAD)
{ if (player != d - FIRSTHEAD && worm[d - FIRSTHEAD].mode != ARMOUR)
{ worm[d - FIRSTHEAD].alive = FALSE;
worm[d - FIRSTHEAD].cause = LIGHTNING;
worm[d - FIRSTHEAD].victor = player;
change(xx, yy, EMPTY);
} }
else /* eg. tail */
change(xx, yy, EMPTY);
break;
} } } }
break;
default:
/* assert(0); */
break;
}
return(score);
}
/* Must have blank line at EOF. */